Commit 7ea4486a authored by Stan Hu's avatar Stan Hu

Merge branch '216794-add-a-graphql-endpoint-for-package-registry' into 'master'

Add a graphql endpoint for project packages list

See merge request gitlab-org/gitlab!31344
parents 1b68149f a32da82f
......@@ -6946,6 +6946,76 @@ interface Noteable {
): NoteConnection!
}
"""
Represents a package
"""
type Package {
"""
The created date
"""
createdAt: Time!
"""
The ID of the package
"""
id: ID!
"""
The name of the package
"""
name: String!
"""
The type of the package
"""
packageType: PackageTypeEnum!
"""
The update date
"""
updatedAt: Time!
"""
The version of the package
"""
version: String
}
"""
The connection type for Package.
"""
type PackageConnection {
"""
A list of edges.
"""
edges: [PackageEdge]
"""
A list of nodes.
"""
nodes: [Package]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type PackageEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: Package
}
"""
Represents the sync and verification state of a package file
"""
......@@ -7026,6 +7096,38 @@ type PackageFileRegistryEdge {
node: PackageFileRegistry
}
enum PackageTypeEnum {
"""
Packages from the composer package manager
"""
COMPOSER
"""
Packages from the conan package manager
"""
CONAN
"""
Packages from the maven package manager
"""
MAVEN
"""
Packages from the npm package manager
"""
NPM
"""
Packages from the nuget package manager
"""
NUGET
"""
Packages from the pypi package manager
"""
PYPI
}
"""
Information about pagination in a connection.
"""
......@@ -7723,6 +7825,31 @@ type Project {
"""
openIssuesCount: Int
"""
Packages of the project
"""
packages(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): PackageConnection
"""
Path of the project
"""
......
......@@ -1034,6 +1034,19 @@ Represents a milestone.
| `readNote` | Boolean! | Indicates the user can perform `read_note` on this resource |
| `resolveNote` | Boolean! | Indicates the user can perform `resolve_note` on this resource |
## Package
Represents a package
| Name | Type | Description |
| --- | ---- | ---------- |
| `createdAt` | Time! | The created date |
| `id` | ID! | The ID of the package |
| `name` | String! | The name of the package |
| `packageType` | PackageTypeEnum! | The type of the package |
| `updatedAt` | Time! | The update date |
| `version` | String | The version of the package |
## PackageFileRegistry
Represents the sync and verification state of a package file
......
......@@ -42,6 +42,10 @@ module EE
Hash.new(0).merge(project.requirements.counts_by_state)
end
field :packages, ::Types::PackageType.connection_type, null: true,
description: 'Packages of the project',
resolver: ::Resolvers::PackagesResolver
def self.requirements_available?(project, user)
::Feature.enabled?(:requirements_management, project, default_enabled: true) && Ability.allowed?(user, :read_requirement, project)
end
......
# frozen_string_literal: true
module Resolvers
class PackagesResolver < BaseResolver
type Types::PackageType, null: true
def resolve(**args)
return unless packages_available?(object, current_user)
::Packages::PackagesFinder.new(object).execute
end
private
def packages_available?(object, user)
::Gitlab.config.packages.enabled && object.feature_available?(:packages)
end
end
end
# frozen_string_literal: true
module Types
class PackageType < BaseObject
graphql_name 'Package'
description 'Represents a package'
authorize :read_package
field :id, GraphQL::ID_TYPE, null: false, description: 'The ID of the package'
field :name, GraphQL::STRING_TYPE, null: false, description: 'The name of the package'
field :created_at, Types::TimeType, null: false, description: 'The created date'
field :updated_at, Types::TimeType, null: false, description: 'The update date'
field :version, GraphQL::STRING_TYPE, null: true, description: 'The version of the package'
field :package_type, Types::PackageTypeEnum, null: false, description: 'The type of the package'
end
end
# frozen_string_literal: true
module Types
class PackageTypeEnum < BaseEnum
::Packages::Package.package_types.keys.each do |package_type|
value package_type.to_s.upcase, "Packages from the #{package_type} package manager", value: package_type.to_s
end
end
end
# frozen_string_literal: true
module Packages
class PackagePolicy < BasePolicy
delegate { @subject.project }
end
end
---
title: Add graphql endpoint for project packages list
merge_request: 31344
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
describe Resolvers::PackagesResolver do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:package) { create(:package, project: project) }
describe '#resolve' do
subject(:packages) { resolve(described_class, ctx: { current_user: user }, obj: project) }
context 'when the package feature is enabled' do
before do
stub_licensed_features(packages: true)
end
context 'when the project has the package feature enabled' do
before do
allow(project).to receive(:feature_available?).with(:packages).and_return(true)
end
it { is_expected.to contain_exactly(package) }
end
context 'when the project has the package feature disabled' do
before do
allow(project).to receive(:feature_available?).with(:packages).and_return(false)
end
it { is_expected.to be_nil }
end
end
context 'when the package feature is not enabled' do
before do
stub_licensed_features(packages: false)
end
it { is_expected.to be_nil }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe GitlabSchema.types['PackageTypeEnum'] do
it 'exposes all package types' do
expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER])
end
end
# frozen_string_literal: true
require 'spec_helper'
describe GitlabSchema.types['Package'] do
it { expect(described_class.graphql_name).to eq('Package') }
it 'includes all the package fields' do
expected_fields = %w[
id name version created_at updated_at package_type
]
expect(described_class).to include_graphql_fields(*expected_fields)
end
end
......@@ -16,7 +16,7 @@ describe GitlabSchema.types['Project'] do
it 'includes the ee specific fields' do
expected_fields = %w[
service_desk_enabled service_desk_address vulnerabilities
requirement_states_count vulnerability_severities_count
requirement_states_count vulnerability_severities_count packages
]
expect(described_class).to include_graphql_fields(*expected_fields)
......
# frozen_string_literal: true
require 'spec_helper'
describe Packages::PackagePolicy do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:package) { create(:package, project: project) }
subject(:policy) { described_class.new(user, package) }
context 'when the user is part of the project' do
before do
project.add_reporter(user)
end
it 'allows read_package' do
expect(policy).to be_allowed(:read_package)
end
end
context 'when the user is not part of the project' do
it 'disallows read_package for any Package' do
expect(policy).to be_disallowed(:read_package)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'getting a package list for a project' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { create(:user) }
let_it_be(:package) { create(:package, project: project) }
let(:packages_data) { graphql_data['project']['packages']['edges'] }
let(:fields) do
<<~QUERY
edges {
node {
#{all_graphql_fields_for('packages'.classify)}
}
}
QUERY
end
let(:query) do
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
query_graphql_field('packages', {}, fields)
)
end
context 'when package feature is available' do
before do
stub_licensed_features(packages: true)
end
context 'when user has access to the project' do
before do
project.add_reporter(current_user)
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
it 'returns packages successfully' do
expect(packages_data[0]['node']['name']).to eq package.name
end
end
context 'when the user does not have access to the project/packages' do
before do
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
it 'returns nil' do
expect(graphql_data['project']).to be_nil
end
end
context 'when the user is not autenthicated' do
before do
post_graphql(query)
end
it_behaves_like 'a working graphql query'
it 'returns nil' do
expect(graphql_data['project']).to be_nil
end
end
end
context 'when package feature is not available' do
before do
stub_licensed_features(packages: false)
project.add_reporter(current_user)
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
it 'returns nil' do
expect(graphql_data['project']['packages']).to be_nil
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