Commit 6bb31a82 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by David Fernandez

Add dependency link to package type

- source
- test
- schema
- shared context

Changelog: added
parent 465fdc49
# frozen_string_literal: true
module Types
module Packages
class DependencyLinkMetadataType < BaseUnion
graphql_name 'DependencyLinkMetadata'
description 'Represents metadata associated with a dependency link'
possible_types ::Types::Packages::Nuget::DependencyLinkMetadatumType
def self.resolve_type(object, context)
case object
when ::Packages::Nuget::DependencyLinkMetadatum
::Types::Packages::Nuget::DependencyLinkMetadatumType
else
# NOTE: This method must be kept in sync with `PackageDependencyLinkType#metadata`,
# which must never produce data that this discriminator cannot handle.
raise 'Unsupported metadata type'
end
end
end
end
end
# frozen_string_literal: true
module Types
module Packages
module Nuget
class DependencyLinkMetadatumType < BaseObject
graphql_name 'NugetDependencyLinkMetadata'
description 'Nuget dependency link metadata'
authorize :read_package
field :id, ::Types::GlobalIDType[::Packages::Nuget::DependencyLinkMetadatum], null: false, description: 'ID of the metadatum.'
field :target_framework, GraphQL::Types::String, null: false, description: 'Target framework of the depdency link package.'
end
end
end
end
# frozen_string_literal: true
module Types
module Packages
class PackageDependencyLinkType < BaseObject
graphql_name 'PackageDependencyLink'
description 'Represents a package dependency link'
authorize :read_package
field :id, ::Types::GlobalIDType[::Packages::DependencyLink], null: false, description: 'ID of the dependency link.'
field :dependency_type, Types::Packages::PackageDependencyTypeEnum, null: false, description: 'Dependency type.'
field :dependency, Types::Packages::PackageDependencyType, null: true, description: 'Dependency.'
field :metadata, Types::Packages::DependencyLinkMetadataType, null: true, description: 'Dependency link metadata.'
# NOTE: This method must be kept in sync with the union
# type: `Types::Packages::DependencyLinkMetadata`.
#
# `Types::Packages::DependencyLinkMetadata.resolve_type(metadata, ctx)` must never raise.
def metadata
model_class = case object.package.package_type
when 'nuget'
::Packages::Nuget::DependencyLinkMetadatum
end
return unless model_class
# rubocop: disable CodeReuse/ActiveRecord
BatchLoader::GraphQL.for(object.id).batch do |ids, loader|
results = model_class.where(dependency_link_id: ids)
results.each { |record| loader.call(record.dependency_link_id, record) }
end
# rubocop: enable CodeReuse/ActiveRecord
end
def dependency
::Gitlab::Graphql::Loaders::BatchModelLoader.new(::Packages::Dependency, object.dependency_id).find
end
end
end
end
# frozen_string_literal: true
# this model does not have any kind of authorization so we disable it
# rubocop:disable Graphql/AuthorizeTypes
module Types
module Packages
class PackageDependencyType < BaseObject
graphql_name 'PackageDependency'
description 'Represents a package dependency.'
field :id, ::Types::GlobalIDType[::Packages::Dependency], null: false, description: 'ID of the dependency.'
field :name, GraphQL::Types::String, null: false, description: 'Name of the dependency.'
field :version_pattern, GraphQL::Types::String, null: false, description: 'Version pattern of the dependency.'
end
end
end
# frozen_string_literal: true
module Types
module Packages
class PackageDependencyTypeEnum < BaseEnum
graphql_name 'PackageDependencyType'
::Packages::DependencyLink.dependency_types.keys.each do |type|
value type.to_s.underscore.upcase, description: "#{type} dependency type", value: type.to_s
end
end
end
end
...@@ -12,6 +12,8 @@ module Types ...@@ -12,6 +12,8 @@ module Types
field :package_files, Types::Packages::PackageFileType.connection_type, null: true, description: 'Package files.' field :package_files, Types::Packages::PackageFileType.connection_type, null: true, description: 'Package files.'
field :dependency_links, Types::Packages::PackageDependencyLinkType.connection_type, null: true, description: 'Dependency link.'
def versions def versions
object.versions object.versions
end end
......
# frozen_string_literal: true
module Packages
class DependencyLinkPolicy < BasePolicy
delegate { @subject.package }
end
end
# frozen_string_literal: true
module Packages
module Nuget
class DependencyLinkMetadatumPolicy < BasePolicy
delegate { @subject.dependency_link.package }
end
end
end
...@@ -6237,6 +6237,29 @@ The connection type for [`Package`](#package). ...@@ -6237,6 +6237,29 @@ The connection type for [`Package`](#package).
| <a id="packageconnectionnodes"></a>`nodes` | [`[Package]`](#package) | A list of nodes. | | <a id="packageconnectionnodes"></a>`nodes` | [`[Package]`](#package) | A list of nodes. |
| <a id="packageconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | | <a id="packageconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `PackageDependencyLinkConnection`
The connection type for [`PackageDependencyLink`](#packagedependencylink).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedependencylinkconnectionedges"></a>`edges` | [`[PackageDependencyLinkEdge]`](#packagedependencylinkedge) | A list of edges. |
| <a id="packagedependencylinkconnectionnodes"></a>`nodes` | [`[PackageDependencyLink]`](#packagedependencylink) | A list of nodes. |
| <a id="packagedependencylinkconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `PackageDependencyLinkEdge`
The edge type for [`PackageDependencyLink`](#packagedependencylink).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedependencylinkedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="packagedependencylinkedgenode"></a>`node` | [`PackageDependencyLink`](#packagedependencylink) | The item at the end of the edge. |
#### `PackageEdge` #### `PackageEdge`
The edge type for [`Package`](#package). The edge type for [`Package`](#package).
...@@ -11330,6 +11353,17 @@ Represents the network policy. ...@@ -11330,6 +11353,17 @@ Represents the network policy.
| <a id="notepermissionsrepositionnote"></a>`repositionNote` | [`Boolean!`](#boolean) | Indicates the user can perform `reposition_note` on this resource. | | <a id="notepermissionsrepositionnote"></a>`repositionNote` | [`Boolean!`](#boolean) | Indicates the user can perform `reposition_note` on this resource. |
| <a id="notepermissionsresolvenote"></a>`resolveNote` | [`Boolean!`](#boolean) | Indicates the user can perform `resolve_note` on this resource. | | <a id="notepermissionsresolvenote"></a>`resolveNote` | [`Boolean!`](#boolean) | Indicates the user can perform `resolve_note` on this resource. |
### `NugetDependencyLinkMetadata`
Nuget dependency link metadata.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="nugetdependencylinkmetadataid"></a>`id` | [`PackagesNugetDependencyLinkMetadatumID!`](#packagesnugetdependencylinkmetadatumid) | ID of the metadatum. |
| <a id="nugetdependencylinkmetadatatargetframework"></a>`targetFramework` | [`String!`](#string) | Target framework of the depdency link package. |
### `NugetMetadata` ### `NugetMetadata`
Nuget metadata. Nuget metadata.
...@@ -11401,6 +11435,31 @@ Represents a composer JSON file. ...@@ -11401,6 +11435,31 @@ Represents a composer JSON file.
| <a id="packagecomposerjsontypetype"></a>`type` | [`String`](#string) | The type set in the Composer JSON file. | | <a id="packagecomposerjsontypetype"></a>`type` | [`String`](#string) | The type set in the Composer JSON file. |
| <a id="packagecomposerjsontypeversion"></a>`version` | [`String`](#string) | The version set in the Composer JSON file. | | <a id="packagecomposerjsontypeversion"></a>`version` | [`String`](#string) | The version set in the Composer JSON file. |
### `PackageDependency`
Represents a package dependency.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedependencyid"></a>`id` | [`PackagesDependencyID!`](#packagesdependencyid) | ID of the dependency. |
| <a id="packagedependencyname"></a>`name` | [`String!`](#string) | Name of the dependency. |
| <a id="packagedependencyversionpattern"></a>`versionPattern` | [`String!`](#string) | Version pattern of the dependency. |
### `PackageDependencyLink`
Represents a package dependency link.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="packagedependencylinkdependency"></a>`dependency` | [`PackageDependency`](#packagedependency) | Dependency. |
| <a id="packagedependencylinkdependencytype"></a>`dependencyType` | [`PackageDependencyType!`](#packagedependencytype) | Dependency type. |
| <a id="packagedependencylinkid"></a>`id` | [`PackagesDependencyLinkID!`](#packagesdependencylinkid) | ID of the dependency link. |
| <a id="packagedependencylinkmetadata"></a>`metadata` | [`DependencyLinkMetadata`](#dependencylinkmetadata) | Dependency link metadata. |
### `PackageDetailsType` ### `PackageDetailsType`
Represents a package details in the Package Registry. Note that this type is in beta and susceptible to changes. Represents a package details in the Package Registry. Note that this type is in beta and susceptible to changes.
...@@ -11410,6 +11469,7 @@ Represents a package details in the Package Registry. Note that this type is in ...@@ -11410,6 +11469,7 @@ Represents a package details in the Package Registry. Note that this type is in
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="packagedetailstypecreatedat"></a>`createdAt` | [`Time!`](#time) | Date of creation. | | <a id="packagedetailstypecreatedat"></a>`createdAt` | [`Time!`](#time) | Date of creation. |
| <a id="packagedetailstypedependencylinks"></a>`dependencyLinks` | [`PackageDependencyLinkConnection`](#packagedependencylinkconnection) | Dependency link. (see [Connections](#connections)) |
| <a id="packagedetailstypeid"></a>`id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. | | <a id="packagedetailstypeid"></a>`id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. |
| <a id="packagedetailstypemetadata"></a>`metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. | | <a id="packagedetailstypemetadata"></a>`metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. |
| <a id="packagedetailstypename"></a>`name` | [`String!`](#string) | Name of the package. | | <a id="packagedetailstypename"></a>`name` | [`String!`](#string) | Name of the package. |
...@@ -15330,6 +15390,15 @@ Rotation length unit of an on-call rotation. ...@@ -15330,6 +15390,15 @@ Rotation length unit of an on-call rotation.
| <a id="oncallrotationunitenumhours"></a>`HOURS` | Hours. | | <a id="oncallrotationunitenumhours"></a>`HOURS` | Hours. |
| <a id="oncallrotationunitenumweeks"></a>`WEEKS` | Weeks. | | <a id="oncallrotationunitenumweeks"></a>`WEEKS` | Weeks. |
### `PackageDependencyType`
| Value | Description |
| ----- | ----------- |
| <a id="packagedependencytypebundle_dependencies"></a>`BUNDLE_DEPENDENCIES` | bundleDependencies dependency type. |
| <a id="packagedependencytypedependencies"></a>`DEPENDENCIES` | dependencies dependency type. |
| <a id="packagedependencytypedev_dependencies"></a>`DEV_DEPENDENCIES` | devDependencies dependency type. |
| <a id="packagedependencytypepeer_dependencies"></a>`PEER_DEPENDENCIES` | peerDependencies dependency type. |
### `PackageGroupSort` ### `PackageGroupSort`
Values for sorting group packages. Values for sorting group packages.
...@@ -16204,12 +16273,30 @@ A `PackagesConanMetadatumID` is a global ID. It is encoded as a string. ...@@ -16204,12 +16273,30 @@ A `PackagesConanMetadatumID` is a global ID. It is encoded as a string.
An example `PackagesConanMetadatumID` is: `"gid://gitlab/Packages::Conan::Metadatum/1"`. An example `PackagesConanMetadatumID` is: `"gid://gitlab/Packages::Conan::Metadatum/1"`.
### `PackagesDependencyID`
A `PackagesDependencyID` is a global ID. It is encoded as a string.
An example `PackagesDependencyID` is: `"gid://gitlab/Packages::Dependency/1"`.
### `PackagesDependencyLinkID`
A `PackagesDependencyLinkID` is a global ID. It is encoded as a string.
An example `PackagesDependencyLinkID` is: `"gid://gitlab/Packages::DependencyLink/1"`.
### `PackagesMavenMetadatumID` ### `PackagesMavenMetadatumID`
A `PackagesMavenMetadatumID` is a global ID. It is encoded as a string. A `PackagesMavenMetadatumID` is a global ID. It is encoded as a string.
An example `PackagesMavenMetadatumID` is: `"gid://gitlab/Packages::Maven::Metadatum/1"`. An example `PackagesMavenMetadatumID` is: `"gid://gitlab/Packages::Maven::Metadatum/1"`.
### `PackagesNugetDependencyLinkMetadatumID`
A `PackagesNugetDependencyLinkMetadatumID` is a global ID. It is encoded as a string.
An example `PackagesNugetDependencyLinkMetadatumID` is: `"gid://gitlab/Packages::Nuget::DependencyLinkMetadatum/1"`.
### `PackagesNugetMetadatumID` ### `PackagesNugetMetadatumID`
A `PackagesNugetMetadatumID` is a global ID. It is encoded as a string. A `PackagesNugetMetadatumID` is a global ID. It is encoded as a string.
...@@ -16339,6 +16426,14 @@ abstract types. ...@@ -16339,6 +16426,14 @@ abstract types.
### Unions ### Unions
#### `DependencyLinkMetadata`
Represents metadata associated with a dependency link.
One of:
- [`NugetDependencyLinkMetadata`](#nugetdependencylinkmetadata)
#### `Issuable` #### `Issuable`
Represents an issuable. Represents an issuable.
......
...@@ -99,6 +99,48 @@ ...@@ -99,6 +99,48 @@
"status": { "status": {
"type": ["string"], "type": ["string"],
"enum": ["DEFAULT", "HIDDEN", "PROCESSING", "ERROR"] "enum": ["DEFAULT", "HIDDEN", "PROCESSING", "ERROR"]
},
"dependencyLinks": {
"type": "object",
"additionalProperties": false,
"properties": {
"pageInfo": { "type": "object" },
"edges": { "type": "array" },
"nodes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"dependencyType": {
"type": "string"
},
"dependency": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"versionPattern": {
"type": "string"
}
}
},
"metadata": {
"anyOf": [
{ "$ref": "./package_nuget_dependency_link_metadata.json" },
{ "type": "null" }
]
}
}
}
}
}
} }
} }
} }
{
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string"
},
"targetFramework": {
"type": "string"
}
}
}
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['NugetDependencyLinkMetadata'] do
it 'includes nuget dependency link metadatum fields' do
expected_fields = %w[
id target_framework
]
expect(described_class).to include_graphql_fields(*expected_fields)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageDependencyLink'] do
it 'includes package file fields' do
expected_fields = %w[
id dependency_type dependency metadata
]
expect(described_class).to include_graphql_fields(*expected_fields)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageDependencyType'] do
it 'exposes all depeendency type values' do
expect(described_class.values.keys).to contain_exactly(*%w[DEPENDENCIES DEV_DEPENDENCIES BUNDLE_DEPENDENCIES PEER_DEPENDENCIES])
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageDependency'] do
it 'includes package file fields' do
expected_fields = %w[
id name version_pattern
]
expect(described_class).to include_graphql_fields(*expected_fields)
end
end
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageDetailsType'] do RSpec.describe GitlabSchema.types['PackageDetailsType'] do
it 'includes all the package fields' do it 'includes all the package fields' do
expected_fields = %w[ expected_fields = %w[
id name version created_at updated_at package_type tags project pipelines versions package_files id name version created_at updated_at package_type tags project pipelines versions package_files dependency_links
] ]
expect(described_class).to include_graphql_fields(*expected_fields) expect(described_class).to include_graphql_fields(*expected_fields)
......
...@@ -6,8 +6,11 @@ RSpec.describe 'nuget package details' do ...@@ -6,8 +6,11 @@ RSpec.describe 'nuget package details' do
include_context 'package details setup' include_context 'package details setup'
let_it_be(:package) { create(:nuget_package, :with_metadatum, project: project) } let_it_be(:package) { create(:nuget_package, :with_metadatum, project: project) }
let_it_be(:dependency_link) { create(:packages_dependency_link, :with_nuget_metadatum, package: package) }
let(:metadata) { query_graphql_fragment('NugetMetadata') } let(:metadata) { query_graphql_fragment('NugetMetadata') }
let(:dependency_link_response) { graphql_data_at(:package, :dependency_links, :nodes, 0) }
let(:dependency_response) { graphql_data_at(:package, :dependency_links, :nodes, 0, :dependency) }
subject { post_graphql(query, current_user: user) } subject { post_graphql(query, current_user: user) }
...@@ -26,4 +29,34 @@ RSpec.describe 'nuget package details' do ...@@ -26,4 +29,34 @@ RSpec.describe 'nuget package details' do
'iconUrl' => package.nuget_metadatum.icon_url 'iconUrl' => package.nuget_metadatum.icon_url
) )
end end
it 'has dependency links' do
expect(dependency_link_response).to include(
'id' => global_id_of(dependency_link),
'dependencyType' => dependency_link.dependency_type.upcase
)
expect(dependency_response).to include(
'id' => global_id_of(dependency_link.dependency),
'name' => dependency_link.dependency.name,
'versionPattern' => dependency_link.dependency.version_pattern
)
end
it 'avoids N+1 queries' do
first_user = create(:user)
second_user = create(:user)
control_count = ActiveRecord::QueryRecorder.new do
post_graphql(query, current_user: first_user)
end
create_list(:packages_dependency_link, 10, :with_nuget_metadatum, package: package)
expect do
post_graphql(query, current_user: second_user)
end.not_to exceed_query_limit(control_count)
expect(response).to have_gitlab_http_status(:ok)
end
end end
...@@ -9,6 +9,7 @@ RSpec.shared_context 'package details setup' do ...@@ -9,6 +9,7 @@ RSpec.shared_context 'package details setup' do
let(:depth) { 3 } let(:depth) { 3 }
let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
let(:package_files) { all_graphql_fields_for('PackageFile') } let(:package_files) { all_graphql_fields_for('PackageFile') }
let(:dependency_links) { all_graphql_fields_for('PackageDependencyLink') }
let(:user) { project.owner } let(:user) { project.owner }
let(:package_details) { graphql_data_at(:package) } let(:package_details) { graphql_data_at(:package) }
let(:metadata_response) { graphql_data_at(:package, :metadata) } let(:metadata_response) { graphql_data_at(:package, :metadata) }
...@@ -28,6 +29,11 @@ RSpec.shared_context 'package details setup' do ...@@ -28,6 +29,11 @@ RSpec.shared_context 'package details setup' do
#{package_files} #{package_files}
} }
} }
dependencyLinks {
nodes {
#{dependency_links}
}
}
FIELDS FIELDS
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment