Commit 249d0cae authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'versionless-package-api-option' into 'master'

Add option to view versionless packages in API

See merge request gitlab-org/gitlab!50669
parents fe25f955 f91877e2
......@@ -27,9 +27,9 @@ module Packages
.including_tags
.for_projects(group_projects_visible_to_current_user.select(:id))
.processed
.has_version
.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
packages = filter_with_version(packages)
packages = filter_by_package_type(packages)
packages = filter_by_package_name(packages)
packages
......@@ -72,5 +72,11 @@ module Packages
packages.search_by_name(params[:package_name])
end
def filter_with_version(packages)
return packages if params[:include_versionless].present?
packages.has_version
end
end
end
......@@ -18,7 +18,7 @@ module Packages
.including_project_route
.including_tags
.processed
.has_version
packages = filter_with_version(packages)
packages = filter_by_package_type(packages)
packages = filter_by_package_name(packages)
packages = order_packages(packages)
......@@ -27,6 +27,12 @@ module Packages
private
def filter_with_version(packages)
return packages if params[:include_versionless].present?
packages.has_version
end
def filter_by_package_type(packages)
return packages unless params[:package_type]
......
---
title: Add include_versionless param to the Package API
merge_request: 50669
author:
type: added
......@@ -28,6 +28,7 @@ GET /projects/:id/packages
| `sort` | string | no | The direction of the order, either `asc` (default) for ascending order or `desc` for descending order. |
| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm`, `pypi`, `composer`, `nuget`, or `golang`. (_Introduced in GitLab 12.9_)
| `package_name` | string | no | Filter the project packages with a fuzzy search by name. (_Introduced in GitLab 12.9_)
| `include_versionless` | boolean | no | When set to true, versionless packages are included in the response. (_Introduced in GitLab 13.8_)
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/:id/packages"
......@@ -88,6 +89,7 @@ GET /groups/:id/packages
| `sort` | string | no | The direction of the order, either `asc` (default) for ascending order or `desc` for descending order. |
| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm`, `pypi`, `composer`, `nuget`, or `golang`. (_Introduced in GitLab 12.9_) |
| `package_name` | string | no | Filter the project packages with a fuzzy search by name. (_[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30980) in GitLab 13.0_)
| `include_versionless` | boolean | no | When set to true, versionless packages are included in the response. (_Introduced in GitLab 13.8_)
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/packages?exclude_subgroups=true"
......
......@@ -31,12 +31,14 @@ module API
desc: 'Return packages of a certain type'
optional :package_name, type: String,
desc: 'Return packages with this name'
optional :include_versionless, type: Boolean,
desc: 'Returns packages without a version'
end
get ':id/packages' do
packages = Packages::GroupPackagesFinder.new(
current_user,
user_group,
declared(params).slice(:exclude_subgroups, :order_by, :sort, :package_type, :package_name)
declared(params).slice(:exclude_subgroups, :order_by, :sort, :package_type, :package_name, :include_versionless)
).execute
present paginate(packages), with: ::API::Entities::Package, user: current_user, group: true
......
......@@ -30,11 +30,13 @@ module API
desc: 'Return packages of a certain type'
optional :package_name, type: String,
desc: 'Return packages with this name'
optional :include_versionless, type: Boolean,
desc: 'Returns packages without a version'
end
get ':id/packages' do
packages = ::Packages::PackagesFinder.new(
user_project,
declared_params.slice(:order_by, :sort, :package_type, :package_name)
declared_params.slice(:order_by, :sort, :package_type, :package_name, :include_versionless)
).execute
present paginate(packages), with: ::API::Entities::Package, user: current_user
......
......@@ -127,12 +127,6 @@ RSpec.describe Packages::GroupPackagesFinder do
it { is_expected.to match_array([package1, package2]) }
end
context 'does not include packages without version number' do
let_it_be(:package_without_version) { create(:maven_package, project: project, version: nil) }
it { is_expected.not_to include(package_without_version) }
end
context 'with package_name' do
let_it_be(:named_package) { create(:maven_package, project: project, name: 'maven') }
let(:params) { { package_name: package_name } }
......@@ -151,6 +145,8 @@ RSpec.describe Packages::GroupPackagesFinder do
end
end
end
it_behaves_like 'concerning versionless param'
end
context 'group has package of all types' do
......
......@@ -81,10 +81,6 @@ RSpec.describe ::Packages::PackagesFinder do
it { is_expected.to match_array([conan_package, maven_package]) }
end
context 'does not include packages without version number' do
let_it_be(:package_without_version) { create(:maven_package, project: project, version: nil) }
it { is_expected.not_to include(package_without_version) }
end
it_behaves_like 'concerning versionless param'
end
end
......@@ -6,8 +6,9 @@ RSpec.describe API::GroupPackages do
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, namespace: group, name: 'project A') }
let_it_be(:user) { create(:user) }
let(:params) { {} }
subject { get api(url) }
subject { get api(url), params: params }
describe 'GET /groups/:id/packages' do
let(:url) { "/groups/#{group.id}/packages" }
......@@ -142,6 +143,7 @@ RSpec.describe API::GroupPackages do
it_behaves_like 'returning response status', :bad_request
end
it_behaves_like 'with versionless packages'
it_behaves_like 'does not cause n^2 queries'
end
end
......@@ -11,12 +11,13 @@ RSpec.describe API::ProjectPackages do
let!(:another_package) { create(:npm_package) }
let(:no_package_url) { "/projects/#{project.id}/packages/0" }
let(:wrong_package_url) { "/projects/#{project.id}/packages/#{another_package.id}" }
let(:params) { {} }
describe 'GET /projects/:id/packages' do
let(:url) { "/projects/#{project.id}/packages" }
let(:package_schema) { 'public_api/v4/packages/packages' }
subject { get api(url) }
subject { get api(url), params: params }
context 'without the need for a license' do
context 'project is public' do
......@@ -118,6 +119,7 @@ RSpec.describe API::ProjectPackages do
end
end
it_behaves_like 'with versionless packages'
it_behaves_like 'does not cause n^2 queries'
end
end
......
# frozen_string_literal: true
RSpec.shared_examples 'concerning versionless param' do
let_it_be(:versionless_package) { create(:maven_package, project: project, version: nil) }
it { is_expected.not_to include(versionless_package) }
context 'with valid include_versionless param' do
let(:params) { { include_versionless: true } }
it { is_expected.to include(versionless_package) }
end
context 'with empty include_versionless param' do
let(:params) { { include_versionless: '' } }
it { is_expected.not_to include(versionless_package) }
end
end
......@@ -220,3 +220,45 @@ RSpec.shared_examples 'package workhorse uploads' do
end
end
end
RSpec.shared_examples 'with versionless packages' do
context 'with versionless package' do
let!(:versionless_package) { create(:maven_package, project: project, version: nil) }
shared_examples 'not including the package' do
it 'does not return the package' do
subject
expect(json_response.map { |package| package['id'] }).not_to include(versionless_package.id)
end
end
it_behaves_like 'not including the package'
context 'with include_versionless param' do
context 'with true include_versionless param' do
[true, 'true', 1, '1'].each do |param|
context "for param #{param}" do
let(:params) { super().merge(include_versionless: param) }
it 'returns the package' do
subject
expect(json_response.map { |package| package['id'] }).to include(versionless_package.id)
end
end
end
end
context 'with falsy include_versionless param' do
[false, '', nil, 'false', 0, '0'].each do |param|
context "for param #{param}" do
let(:params) { super().merge(include_versionless: param) }
it_behaves_like 'not including the package'
end
end
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