Commit bfcf5fdc authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Alex Kalderimis

Add sorting for group and project packages type

parent 4313ef4a
...@@ -4,6 +4,24 @@ module Resolvers ...@@ -4,6 +4,24 @@ module Resolvers
class GroupPackagesResolver < BaseResolver class GroupPackagesResolver < BaseResolver
type Types::Packages::PackageType.connection_type, null: true type Types::Packages::PackageType.connection_type, null: true
argument :sort, Types::Packages::PackageGroupSortEnum,
description: 'Sort packages by this criteria.',
required: false,
default_value: :created_desc
SORT_TO_PARAMS_MAP = {
created_desc: { order_by: 'created', sort: 'desc' },
created_asc: { order_by: 'created', sort: 'asc' },
name_desc: { order_by: 'name', sort: 'desc' },
name_asc: { order_by: 'name', sort: 'asc' },
version_desc: { order_by: 'version', sort: 'desc' },
version_asc: { order_by: 'version', sort: 'asc' },
type_desc: { order_by: 'type', sort: 'desc' },
type_asc: { order_by: 'type', sort: 'asc' },
project_path_desc: { order_by: 'project_path', sort: 'desc' },
project_path_asc: { order_by: 'project_path', sort: 'asc' }
}.freeze
def ready?(**args) def ready?(**args)
context[self.class] ||= { executions: 0 } context[self.class] ||= { executions: 0 }
context[self.class][:executions] += 1 context[self.class][:executions] += 1
...@@ -12,10 +30,10 @@ module Resolvers ...@@ -12,10 +30,10 @@ module Resolvers
super super
end end
def resolve(**args) def resolve(sort:)
return unless packages_available? return unless packages_available?
::Packages::GroupPackagesFinder.new(current_user, object).execute ::Packages::GroupPackagesFinder.new(current_user, object, SORT_TO_PARAMS_MAP[sort]).execute
end end
private private
......
...@@ -4,10 +4,26 @@ module Resolvers ...@@ -4,10 +4,26 @@ module Resolvers
class ProjectPackagesResolver < BaseResolver class ProjectPackagesResolver < BaseResolver
type Types::Packages::PackageType.connection_type, null: true type Types::Packages::PackageType.connection_type, null: true
def resolve(**args) argument :sort, Types::Packages::PackageSortEnum,
description: 'Sort packages by this criteria.',
required: false,
default_value: :created_desc
SORT_TO_PARAMS_MAP = {
created_desc: { order_by: 'created', sort: 'desc' },
created_asc: { order_by: 'created', sort: 'asc' },
name_desc: { order_by: 'name', sort: 'desc' },
name_asc: { order_by: 'name', sort: 'asc' },
version_desc: { order_by: 'version', sort: 'desc' },
version_asc: { order_by: 'version', sort: 'asc' },
type_desc: { order_by: 'type', sort: 'desc' },
type_asc: { order_by: 'type', sort: 'asc' }
}.freeze
def resolve(sort:)
return unless packages_available? return unless packages_available?
::Packages::PackagesFinder.new(object).execute ::Packages::PackagesFinder.new(object, SORT_TO_PARAMS_MAP.fetch(sort)).execute
end end
private private
......
# frozen_string_literal: true
module Types
module Packages
class PackageGroupSortEnum < PackageSortEnum
graphql_name 'PackageGroupSort'
description 'Values for sorting group packages'
# The following enums are not available till we enable the new Arel node:
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58657#note_552632305
# value 'PROJECT_PATH_DESC', 'Project by descending order.', value: :project_path_desc
# value 'PROJECT_PATH_ASC', 'Project by ascending order.', value: :project_path_asc
end
end
end
# frozen_string_literal: true
module Types
module Packages
class PackageSortEnum < BaseEnum
graphql_name 'PackageSort'
description 'Values for sorting package'
value 'CREATED_DESC', 'Ordered by created_at in descending order.', value: :created_desc
value 'CREATED_ASC', 'Ordered by created_at in ascending order.', value: :created_asc
value 'NAME_DESC', 'Ordered by name in descending order.', value: :name_desc
value 'NAME_ASC', 'Ordered by name in ascending order.', value: :name_asc
value 'VERSION_DESC', 'Ordered by version in descending order.', value: :version_desc
value 'VERSION_ASC', 'Ordered by version in ascending order.', value: :version_asc
value 'TYPE_DESC', 'Ordered by type in descending order.', value: :type_desc
value 'TYPE_ASC', 'Ordered by type in ascending order.', value: :type_asc
end
end
end
...@@ -122,14 +122,14 @@ class Packages::Package < ApplicationRecord ...@@ -122,14 +122,14 @@ class Packages::Package < ApplicationRecord
scope :select_distinct_name, -> { select(:name).distinct } scope :select_distinct_name, -> { select(:name).distinct }
# Sorting # Sorting
scope :order_created, -> { reorder('created_at ASC') } scope :order_created, -> { reorder(created_at: :asc) }
scope :order_created_desc, -> { reorder('created_at DESC') } scope :order_created_desc, -> { reorder(created_at: :desc) }
scope :order_name, -> { reorder('name ASC') } scope :order_name, -> { reorder(name: :asc) }
scope :order_name_desc, -> { reorder('name DESC') } scope :order_name_desc, -> { reorder(name: :desc) }
scope :order_version, -> { reorder('version ASC') } scope :order_version, -> { reorder(version: :asc) }
scope :order_version_desc, -> { reorder('version DESC') } scope :order_version_desc, -> { reorder(version: :desc) }
scope :order_type, -> { reorder('package_type ASC') } scope :order_type, -> { reorder(package_type: :asc) }
scope :order_type_desc, -> { reorder('package_type DESC') } scope :order_type_desc, -> { reorder(package_type: :desc) }
scope :order_project_name, -> { joins(:project).reorder('projects.name ASC') } scope :order_project_name, -> { joins(:project).reorder('projects.name ASC') }
scope :order_project_name_desc, -> { joins(:project).reorder('projects.name DESC') } scope :order_project_name_desc, -> { joins(:project).reorder('projects.name DESC') }
scope :order_project_path, -> { joins(:project).reorder('projects.path ASC, id ASC') } scope :order_project_path, -> { joins(:project).reorder('projects.path ASC, id ASC') }
......
---
title: Add sorting for group and project packages type
merge_request: 58657
author:
type: added
...@@ -8475,7 +8475,6 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -8475,7 +8475,6 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupmentionsdisabled"></a>`mentionsDisabled` | [`Boolean`](#boolean) | Indicates if a group is disabled from getting mentioned. | | <a id="groupmentionsdisabled"></a>`mentionsDisabled` | [`Boolean`](#boolean) | Indicates if a group is disabled from getting mentioned. |
| <a id="groupname"></a>`name` | [`String!`](#string) | Name of the namespace. | | <a id="groupname"></a>`name` | [`String!`](#string) | Name of the namespace. |
| <a id="grouppackagesettings"></a>`packageSettings` | [`PackageSettings`](#packagesettings) | The package settings for the namespace. | | <a id="grouppackagesettings"></a>`packageSettings` | [`PackageSettings`](#packagesettings) | The package settings for the namespace. |
| <a id="grouppackages"></a>`packages` | [`PackageConnection`](#packageconnection) | Packages of the group. |
| <a id="groupparent"></a>`parent` | [`Group`](#group) | Parent group. | | <a id="groupparent"></a>`parent` | [`Group`](#group) | Parent group. |
| <a id="grouppath"></a>`path` | [`String!`](#string) | Path of the namespace. | | <a id="grouppath"></a>`path` | [`String!`](#string) | Path of the namespace. |
| <a id="groupprojectcreationlevel"></a>`projectCreationLevel` | [`String`](#string) | The permission level required to create projects in the group. | | <a id="groupprojectcreationlevel"></a>`projectCreationLevel` | [`String`](#string) | The permission level required to create projects in the group. |
...@@ -8828,6 +8827,22 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -8828,6 +8827,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupmilestonestimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | <a id="groupmilestonestimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="groupmilestonestitle"></a>`title` | [`String`](#string) | The title of the milestone. | | <a id="groupmilestonestitle"></a>`title` | [`String`](#string) | The title of the milestone. |
##### `Group.packages`
Packages of the group.
Returns [`PackageConnection`](#packageconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="grouppackagessort"></a>`sort` | [`PackageGroupSort`](#packagegroupsort) | Sort packages by this criteria. |
##### `Group.projects` ##### `Group.projects`
Projects within this namespace. Projects within this namespace.
...@@ -10323,7 +10338,6 @@ Represents vulnerability finding of a security report on the pipeline. ...@@ -10323,7 +10338,6 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectonlyallowmergeifalldiscussionsareresolved"></a>`onlyAllowMergeIfAllDiscussionsAreResolved` | [`Boolean`](#boolean) | Indicates if merge requests of the project can only be merged when all the discussions are resolved. | | <a id="projectonlyallowmergeifalldiscussionsareresolved"></a>`onlyAllowMergeIfAllDiscussionsAreResolved` | [`Boolean`](#boolean) | Indicates if merge requests of the project can only be merged when all the discussions are resolved. |
| <a id="projectonlyallowmergeifpipelinesucceeds"></a>`onlyAllowMergeIfPipelineSucceeds` | [`Boolean`](#boolean) | Indicates if merge requests of the project can only be merged with successful jobs. | | <a id="projectonlyallowmergeifpipelinesucceeds"></a>`onlyAllowMergeIfPipelineSucceeds` | [`Boolean`](#boolean) | Indicates if merge requests of the project can only be merged with successful jobs. |
| <a id="projectopenissuescount"></a>`openIssuesCount` | [`Int`](#int) | Number of open issues for the project. | | <a id="projectopenissuescount"></a>`openIssuesCount` | [`Int`](#int) | Number of open issues for the project. |
| <a id="projectpackages"></a>`packages` | [`PackageConnection`](#packageconnection) | Packages of the project. |
| <a id="projectpath"></a>`path` | [`String!`](#string) | Path of the project. | | <a id="projectpath"></a>`path` | [`String!`](#string) | Path of the project. |
| <a id="projectpipelineanalytics"></a>`pipelineAnalytics` | [`PipelineAnalytics`](#pipelineanalytics) | Pipeline analytics. | | <a id="projectpipelineanalytics"></a>`pipelineAnalytics` | [`PipelineAnalytics`](#pipelineanalytics) | Pipeline analytics. |
| <a id="projectprintingmergerequestlinkenabled"></a>`printingMergeRequestLinkEnabled` | [`Boolean`](#boolean) | Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line. | | <a id="projectprintingmergerequestlinkenabled"></a>`printingMergeRequestLinkEnabled` | [`Boolean`](#boolean) | Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line. |
...@@ -10823,6 +10837,22 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -10823,6 +10837,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectmilestonestimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | <a id="projectmilestonestimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="projectmilestonestitle"></a>`title` | [`String`](#string) | The title of the milestone. | | <a id="projectmilestonestitle"></a>`title` | [`String`](#string) | The title of the milestone. |
##### `Project.packages`
Packages of the project.
Returns [`PackageConnection`](#packageconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectpackagessort"></a>`sort` | [`PackageSort`](#packagesort) | Sort packages by this criteria. |
##### `Project.pipeline` ##### `Project.pipeline`
Build pipeline of the project. Build pipeline of the project.
...@@ -13585,6 +13615,36 @@ Rotation length unit of an on-call rotation. ...@@ -13585,6 +13615,36 @@ 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. |
### `PackageGroupSort`
Values for sorting group packages.
| Value | Description |
| ----- | ----------- |
| <a id="packagegroupsortcreated_asc"></a>`CREATED_ASC` | Ordered by created_at in ascending order. |
| <a id="packagegroupsortcreated_desc"></a>`CREATED_DESC` | Ordered by created_at in descending order. |
| <a id="packagegroupsortname_asc"></a>`NAME_ASC` | Ordered by name in ascending order. |
| <a id="packagegroupsortname_desc"></a>`NAME_DESC` | Ordered by name in descending order. |
| <a id="packagegroupsorttype_asc"></a>`TYPE_ASC` | Ordered by type in ascending order. |
| <a id="packagegroupsorttype_desc"></a>`TYPE_DESC` | Ordered by type in descending order. |
| <a id="packagegroupsortversion_asc"></a>`VERSION_ASC` | Ordered by version in ascending order. |
| <a id="packagegroupsortversion_desc"></a>`VERSION_DESC` | Ordered by version in descending order. |
### `PackageSort`
Values for sorting package.
| Value | Description |
| ----- | ----------- |
| <a id="packagesortcreated_asc"></a>`CREATED_ASC` | Ordered by created_at in ascending order. |
| <a id="packagesortcreated_desc"></a>`CREATED_DESC` | Ordered by created_at in descending order. |
| <a id="packagesortname_asc"></a>`NAME_ASC` | Ordered by name in ascending order. |
| <a id="packagesortname_desc"></a>`NAME_DESC` | Ordered by name in descending order. |
| <a id="packagesorttype_asc"></a>`TYPE_ASC` | Ordered by type in ascending order. |
| <a id="packagesorttype_desc"></a>`TYPE_DESC` | Ordered by type in descending order. |
| <a id="packagesortversion_asc"></a>`VERSION_ASC` | Ordered by version in ascending order. |
| <a id="packagesortversion_desc"></a>`VERSION_DESC` | Ordered by version in descending order. |
### `PackageTypeEnum` ### `PackageTypeEnum`
| Value | Description | | Value | Description |
......
...@@ -8,29 +8,28 @@ ...@@ -8,29 +8,28 @@
"packageUsername", "packageUsername",
"packageChannel", "packageChannel",
"recipe", "recipe",
"recipePath", "recipePath"
"packageName"
], ],
"properties": { "properties": {
"id": { "id": {
"type": "string" "type": "string"
}, },
"created_at": { "createdAt": {
"type": "string" "type": "string"
}, },
"updated_at": { "updatedAt": {
"type": "string" "type": "string"
}, },
"package_username": { "packageUsername": {
"type": "string" "type": "string"
}, },
"package_channel": { "packageChannel": {
"type": "string" "type": "string"
}, },
"recipe": { "recipe": {
"type": "string" "type": "string"
}, },
"recipe_path": { "recipePath": {
"type": "string" "type": "string"
} }
} }
......
...@@ -8,11 +8,46 @@ RSpec.describe Resolvers::GroupPackagesResolver do ...@@ -8,11 +8,46 @@ RSpec.describe Resolvers::GroupPackagesResolver do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) } let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, group: group) } let_it_be(:project) { create(:project, :public, group: group) }
let_it_be(:package) { create(:package, project: project) }
let(:args) do
{ sort: :created_desc }
end
describe '#resolve' do describe '#resolve' do
subject(:packages) { resolve(described_class, ctx: { current_user: user }, obj: group) } subject { resolve(described_class, ctx: { current_user: user }, obj: group, args: args).to_a }
context 'without sort' do
let_it_be(:package) { create(:package, project: project) }
it { is_expected.to contain_exactly(package) }
end
context 'with a sort argument' do
let_it_be(:project2) { create(:project, :public, group: group) }
let_it_be(:sort_repository) do
create(:conan_package, name: 'bar', project: project, created_at: 1.day.ago, version: "1.0.0")
end
let_it_be(:sort_repository2) do
create(:maven_package, name: 'foo', project: project2, created_at: 1.hour.ago, version: "2.0.0")
end
[:created_desc, :name_desc, :version_desc, :type_asc, :project_path_desc].each do |order|
context "#{order}" do
let(:args) { { sort: order } }
it { is_expected.to eq([sort_repository2, sort_repository]) }
end
end
[:created_asc, :name_asc, :version_asc, :type_desc, :project_path_asc].each do |order|
context "#{order}" do
let(:args) { { sort: order } }
it { is_expected.to contain_exactly(package) } it { is_expected.to eq([sort_repository, sort_repository2]) }
end
end
end
end end
end end
...@@ -7,11 +7,44 @@ RSpec.describe Resolvers::ProjectPackagesResolver do ...@@ -7,11 +7,44 @@ RSpec.describe Resolvers::ProjectPackagesResolver do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) } let_it_be(:project) { create(:project, :public) }
let_it_be(:package) { create(:package, project: project) }
let(:args) do
{ sort: :created_desc }
end
describe '#resolve' do describe '#resolve' do
subject(:packages) { resolve(described_class, ctx: { current_user: user }, obj: project) } subject { resolve(described_class, ctx: { current_user: user }, obj: project, args: args).to_a }
context 'without sort' do
let_it_be(:package) { create(:package, project: project) }
it { is_expected.to contain_exactly(package) }
end
context 'with a sort argument' do
let_it_be(:sort_repository) do
create(:conan_package, name: 'bar', project: project, created_at: 1.day.ago, version: "1.0.0")
end
let_it_be(:sort_repository2) do
create(:maven_package, name: 'foo', project: project, created_at: 1.hour.ago, version: "2.0.0")
end
[:created_desc, :name_desc, :version_desc, :type_asc].each do |order|
context "#{order}" do
let(:args) { { sort: order } }
it { is_expected.to eq([sort_repository2, sort_repository]) }
end
end
[:created_asc, :name_asc, :version_asc, :type_desc].each do |order|
context "#{order}" do
let(:args) { { sort: order } }
it { is_expected.to contain_exactly(package) } it { is_expected.to eq([sort_repository, sort_repository2]) }
end
end
end
end end
end end
...@@ -12,11 +12,11 @@ RSpec.describe 'getting a package list for a group' do ...@@ -12,11 +12,11 @@ RSpec.describe 'getting a package list for a group' do
let_it_be(:group_two_project) { create(:project, :repository, group: group_two) } let_it_be(:group_two_project) { create(:project, :repository, group: group_two) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:package) { create(:package, project: project) }
let_it_be(:npm_package) { create(:npm_package, project: group_two_project) } let_it_be(:npm_package) { create(:npm_package, project: group_two_project) }
let_it_be(:maven_package) { create(:maven_package, project: project) } let_it_be(:maven_package) { create(:maven_package, project: project, name: 'tab', version: '4.0.0', created_at: 5.days.ago) }
let_it_be(:debian_package) { create(:debian_package, project: another_project) } let_it_be(:package) { create(:npm_package, project: project, name: 'uab', version: '5.0.0', created_at: 4.days.ago) }
let_it_be(:composer_package) { create(:composer_package, project: another_project) } let_it_be(:composer_package) { create(:composer_package, project: another_project, name: 'vab', version: '6.0.0', created_at: 3.days.ago) }
let_it_be(:debian_package) { create(:debian_package, project: another_project, name: 'zab', version: '7.0.0', created_at: 2.days.ago) }
let_it_be(:composer_metadatum) do let_it_be(:composer_metadatum) do
create(:composer_metadatum, package: composer_package, create(:composer_metadatum, package: composer_package,
target_sha: 'afdeh', target_sha: 'afdeh',
...@@ -46,6 +46,42 @@ RSpec.describe 'getting a package list for a group' do ...@@ -46,6 +46,42 @@ RSpec.describe 'getting a package list for a group' do
it_behaves_like 'group and project packages query' it_behaves_like 'group and project packages query'
describe 'sorting and pagination' do
let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} }
let(:data_path) { [:group, :packages] }
before do
resource.add_reporter(current_user)
end
[:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order|
context "#{order}" do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { order }
let(:first_param) { 4 }
let(:expected_results) { ascending_packages }
end
end
end
[:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order|
context "#{order}" do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { order }
let(:first_param) { 4 }
let(:expected_results) { ascending_packages.reverse }
end
end
end
def pagination_query(params)
graphql_query_for(:group, { 'fullPath' => resource.full_path },
query_nodes(:packages, :id, include_pagination_info: true, args: params)
)
end
end
context 'with a batched query' do context 'with a batched query' do
let(:batch_query) do let(:batch_query) do
<<~QUERY <<~QUERY
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'package details' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:composer_package) { create(:composer_package, project: project) }
let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } }
let_it_be(:composer_metadatum) do
# we are forced to manually create the metadatum, without using the factory to force the sha to be a string
# and avoid an error where gitaly can't find the repository
create(:composer_metadatum, package: composer_package, target_sha: 'foo_sha', composer_json: composer_json)
end
let(:depth) { 3 }
let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
let(:metadata) { query_graphql_fragment('ComposerMetadata') }
let(:package_files) { all_graphql_fields_for('PackageFile') }
let(:user) { project.owner }
let(:package_global_id) { global_id_of(composer_package) }
let(:package_details) { graphql_data_at(:package) }
let(:metadata_response) { graphql_data_at(:package, :metadata) }
let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) }
let(:query) do
graphql_query_for(:package, { id: package_global_id }, <<~FIELDS)
#{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)}
metadata {
#{metadata}
}
packageFiles {
nodes {
#{package_files}
}
}
FIELDS
end
subject { post_graphql(query, current_user: user) }
before do
subject
end
it_behaves_like 'a working graphql query' do
it 'matches the JSON schema' do
expect(package_details).to match_schema('graphql/packages/package_details')
end
end
describe 'Composer' do
it 'has the correct metadata' do
expect(metadata_response).to include(
'targetSha' => 'foo_sha',
'composerJson' => composer_json.transform_keys(&:to_s).transform_values(&:to_s)
)
end
it 'does not have files' do
expect(package_files_response).to be_empty
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'conan package details' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:conan_package) { create(:conan_package, project: project) }
let(:package_global_id) { global_id_of(conan_package) }
let(:metadata) { query_graphql_fragment('ConanMetadata') }
let(:first_file) { conan_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } }
let(:depth) { 3 }
let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
let(:package_files) { all_graphql_fields_for('PackageFile') }
let(:package_files_metadata) {query_graphql_fragment('ConanFileMetadata')}
let(:user) { project.owner }
let(:package_details) { graphql_data_at(:package) }
let(:metadata_response) { graphql_data_at(:package, :metadata) }
let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) }
let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)}
let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata)}
let(:query) do
graphql_query_for(:package, { id: package_global_id }, <<~FIELDS)
#{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)}
metadata {
#{metadata}
}
packageFiles {
nodes {
#{package_files}
fileMetadata {
#{package_files_metadata}
}
}
}
FIELDS
end
subject { post_graphql(query, current_user: user) }
before do
subject
end
it_behaves_like 'a working graphql query' do
it 'matches the JSON schema' do
expect(package_details).to match_schema('graphql/packages/package_details')
end
end
it 'has the correct metadata' do
expect(metadata_response).to include(
'id' => global_id_of(conan_package.conan_metadatum),
'recipe' => conan_package.conan_metadatum.recipe,
'packageChannel' => conan_package.conan_metadatum.package_channel,
'packageUsername' => conan_package.conan_metadatum.package_username,
'recipePath' => conan_package.conan_metadatum.recipe_path
)
end
it 'has the right amount of files' do
expect(package_files_response.length).to be(conan_package.package_files.length)
end
it 'has the basic package files data' do
expect(first_file_response).to include(
'id' => global_id_of(first_file),
'fileName' => first_file.file_name,
'size' => first_file.size.to_s,
'downloadPath' => first_file.download_path,
'fileSha1' => first_file.file_sha1,
'fileMd5' => first_file.file_md5,
'fileSha256' => first_file.file_sha256
)
end
it 'has the correct file metadata' do
expect(first_file_response_metadata).to include(
'id' => global_id_of(first_file.conan_file_metadatum),
'packageRevision' => first_file.conan_file_metadatum.package_revision,
'conanPackageReference' => first_file.conan_file_metadatum.conan_package_reference,
'recipeRevision' => first_file.conan_file_metadatum.recipe_revision,
'conanFileType' => first_file.conan_file_metadatum.conan_file_type.upcase
)
end
end
...@@ -17,7 +17,9 @@ RSpec.describe 'package details' do ...@@ -17,7 +17,9 @@ RSpec.describe 'package details' do
let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] }
let(:metadata) { query_graphql_fragment('ComposerMetadata') } let(:metadata) { query_graphql_fragment('ComposerMetadata') }
let(:package_files) {all_graphql_fields_for('PackageFile')} let(:package_files) {all_graphql_fields_for('PackageFile')}
let(:package_files_metadata) {query_graphql_fragment('ConanFileMetadata')} let(:user) { project.owner }
let(:package_global_id) { global_id_of(composer_package) }
let(:package_details) { graphql_data_at(:package) }
let(:query) do let(:query) do
graphql_query_for(:package, { id: package_global_id }, <<~FIELDS) graphql_query_for(:package, { id: package_global_id }, <<~FIELDS)
...@@ -28,22 +30,11 @@ RSpec.describe 'package details' do ...@@ -28,22 +30,11 @@ RSpec.describe 'package details' do
packageFiles { packageFiles {
nodes { nodes {
#{package_files} #{package_files}
fileMetadata {
#{package_files_metadata}
}
} }
} }
FIELDS FIELDS
end end
let(:user) { project.owner }
let(:package_global_id) { global_id_of(composer_package) }
let(:package_details) { graphql_data_at(:package) }
let(:metadata_response) { graphql_data_at(:package, :metadata) }
let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) }
let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)}
let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata)}
subject { post_graphql(query, current_user: user) } subject { post_graphql(query, current_user: user) }
it_behaves_like 'a working graphql query' do it_behaves_like 'a working graphql query' do
...@@ -56,69 +47,6 @@ RSpec.describe 'package details' do ...@@ -56,69 +47,6 @@ RSpec.describe 'package details' do
end end
end end
describe 'Packages Metadata' do
before do
subject
end
describe 'Composer' do
it 'has the correct metadata' do
expect(metadata_response).to include(
'targetSha' => 'foo_sha',
'composerJson' => composer_json.transform_keys(&:to_s).transform_values(&:to_s)
)
end
it 'does not have files' do
expect(package_files_response).to be_empty
end
end
describe 'Conan' do
let_it_be(:conan_package) { create(:conan_package, project: project) }
let(:package_global_id) { global_id_of(conan_package) }
let(:metadata) { query_graphql_fragment('ConanMetadata') }
let(:first_file) { conan_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } }
it 'has the correct metadata' do
expect(metadata_response).to include(
'id' => global_id_of(conan_package.conan_metadatum),
'recipe' => conan_package.conan_metadatum.recipe,
'packageChannel' => conan_package.conan_metadatum.package_channel,
'packageUsername' => conan_package.conan_metadatum.package_username,
'recipePath' => conan_package.conan_metadatum.recipe_path
)
end
it 'has the right amount of files' do
expect(package_files_response.length).to be(conan_package.package_files.length)
end
it 'has the basic package files data' do
expect(first_file_response).to include(
'id' => global_id_of(first_file),
'fileName' => first_file.file_name,
'size' => first_file.size.to_s,
'downloadPath' => first_file.download_path,
'fileSha1' => first_file.file_sha1,
'fileMd5' => first_file.file_md5,
'fileSha256' => first_file.file_sha256
)
end
it 'has the correct file metadata' do
expect(first_file_response_metadata).to include(
'id' => global_id_of(first_file.conan_file_metadatum),
'packageRevision' => first_file.conan_file_metadatum.package_revision,
'conanPackageReference' => first_file.conan_file_metadatum.conan_package_reference,
'recipeRevision' => first_file.conan_file_metadatum.recipe_revision,
'conanFileType' => first_file.conan_file_metadatum.conan_file_type.upcase
)
end
end
end
context 'there are other versions of this package' do context 'there are other versions of this package' do
let(:depth) { 3 } let(:depth) { 3 }
let(:excluded) { %w[metadata project tags pipelines] } # to limit the query complexity let(:excluded) { %w[metadata project tags pipelines] } # to limit the query complexity
......
...@@ -7,11 +7,10 @@ RSpec.describe 'getting a package list for a project' do ...@@ -7,11 +7,10 @@ RSpec.describe 'getting a package list for a project' do
let_it_be(:resource) { create(:project, :repository) } let_it_be(:resource) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:maven_package) { create(:maven_package, project: resource, name: 'tab', version: '4.0.0', created_at: 5.days.ago) }
let_it_be(:package) { create(:package, project: resource) } let_it_be(:package) { create(:npm_package, project: resource, name: 'uab', version: '5.0.0', created_at: 4.days.ago) }
let_it_be(:maven_package) { create(:maven_package, project: resource) } let_it_be(:composer_package) { create(:composer_package, project: resource, name: 'vab', version: '6.0.0', created_at: 3.days.ago) }
let_it_be(:debian_package) { create(:debian_package, project: resource) } let_it_be(:debian_package) { create(:debian_package, project: resource, name: 'zab', version: '7.0.0', created_at: 2.days.ago) }
let_it_be(:composer_package) { create(:composer_package, project: resource) }
let_it_be(:composer_metadatum) do let_it_be(:composer_metadatum) do
create(:composer_metadatum, package: composer_package, create(:composer_metadatum, package: composer_package,
target_sha: 'afdeh', target_sha: 'afdeh',
...@@ -40,4 +39,40 @@ RSpec.describe 'getting a package list for a project' do ...@@ -40,4 +39,40 @@ RSpec.describe 'getting a package list for a project' do
end end
it_behaves_like 'group and project packages query' it_behaves_like 'group and project packages query'
describe 'sorting and pagination' do
let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} }
let(:data_path) { [:project, :packages] }
before do
resource.add_reporter(current_user)
end
[:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order|
context "#{order}" do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { order }
let(:first_param) { 4 }
let(:expected_results) { ascending_packages }
end
end
end
[:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order|
context "#{order}" do
it_behaves_like 'sorted paginated query' do
let(:sort_param) { order }
let(:first_param) { 4 }
let(:expected_results) { ascending_packages.reverse }
end
end
end
def pagination_query(params)
graphql_query_for(:project, { 'fullPath' => resource.full_path },
query_nodes(:packages, :id, include_pagination_info: true, args: params)
)
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