Commit f91877e2 authored by Steve Abrams's avatar Steve Abrams Committed by Mayra Cabrera

Add option to view versionless packages in API

Adds include_versionless param to project and group
package list apis.
parent 4b35fcc6
...@@ -27,9 +27,9 @@ module Packages ...@@ -27,9 +27,9 @@ module Packages
.including_tags .including_tags
.for_projects(group_projects_visible_to_current_user.select(:id)) .for_projects(group_projects_visible_to_current_user.select(:id))
.processed .processed
.has_version
.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}") .sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
packages = filter_with_version(packages)
packages = filter_by_package_type(packages) packages = filter_by_package_type(packages)
packages = filter_by_package_name(packages) packages = filter_by_package_name(packages)
packages packages
...@@ -72,5 +72,11 @@ module Packages ...@@ -72,5 +72,11 @@ module Packages
packages.search_by_name(params[:package_name]) packages.search_by_name(params[:package_name])
end end
def filter_with_version(packages)
return packages if params[:include_versionless].present?
packages.has_version
end
end end
end end
...@@ -18,7 +18,7 @@ module Packages ...@@ -18,7 +18,7 @@ module Packages
.including_project_route .including_project_route
.including_tags .including_tags
.processed .processed
.has_version packages = filter_with_version(packages)
packages = filter_by_package_type(packages) packages = filter_by_package_type(packages)
packages = filter_by_package_name(packages) packages = filter_by_package_name(packages)
packages = order_packages(packages) packages = order_packages(packages)
...@@ -27,6 +27,12 @@ module Packages ...@@ -27,6 +27,12 @@ module Packages
private private
def filter_with_version(packages)
return packages if params[:include_versionless].present?
packages.has_version
end
def filter_by_package_type(packages) def filter_by_package_type(packages)
return packages unless params[:package_type] 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 ...@@ -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. | | `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_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_) | `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 ```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/:id/packages" curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/:id/packages"
...@@ -88,6 +89,7 @@ GET /groups/: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. | | `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_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_) | `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 ```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/packages?exclude_subgroups=true" 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 ...@@ -31,12 +31,14 @@ module API
desc: 'Return packages of a certain type' desc: 'Return packages of a certain type'
optional :package_name, type: String, optional :package_name, type: String,
desc: 'Return packages with this name' desc: 'Return packages with this name'
optional :include_versionless, type: Boolean,
desc: 'Returns packages without a version'
end end
get ':id/packages' do get ':id/packages' do
packages = Packages::GroupPackagesFinder.new( packages = Packages::GroupPackagesFinder.new(
current_user, current_user,
user_group, 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 ).execute
present paginate(packages), with: ::API::Entities::Package, user: current_user, group: true present paginate(packages), with: ::API::Entities::Package, user: current_user, group: true
......
...@@ -30,11 +30,13 @@ module API ...@@ -30,11 +30,13 @@ module API
desc: 'Return packages of a certain type' desc: 'Return packages of a certain type'
optional :package_name, type: String, optional :package_name, type: String,
desc: 'Return packages with this name' desc: 'Return packages with this name'
optional :include_versionless, type: Boolean,
desc: 'Returns packages without a version'
end end
get ':id/packages' do get ':id/packages' do
packages = ::Packages::PackagesFinder.new( packages = ::Packages::PackagesFinder.new(
user_project, 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 ).execute
present paginate(packages), with: ::API::Entities::Package, user: current_user present paginate(packages), with: ::API::Entities::Package, user: current_user
......
...@@ -127,12 +127,6 @@ RSpec.describe Packages::GroupPackagesFinder do ...@@ -127,12 +127,6 @@ RSpec.describe Packages::GroupPackagesFinder do
it { is_expected.to match_array([package1, package2]) } it { is_expected.to match_array([package1, package2]) }
end 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 context 'with package_name' do
let_it_be(:named_package) { create(:maven_package, project: project, name: 'maven') } let_it_be(:named_package) { create(:maven_package, project: project, name: 'maven') }
let(:params) { { package_name: package_name } } let(:params) { { package_name: package_name } }
...@@ -151,6 +145,8 @@ RSpec.describe Packages::GroupPackagesFinder do ...@@ -151,6 +145,8 @@ RSpec.describe Packages::GroupPackagesFinder do
end end
end end
end end
it_behaves_like 'concerning versionless param'
end end
context 'group has package of all types' do context 'group has package of all types' do
......
...@@ -81,10 +81,6 @@ RSpec.describe ::Packages::PackagesFinder do ...@@ -81,10 +81,6 @@ RSpec.describe ::Packages::PackagesFinder do
it { is_expected.to match_array([conan_package, maven_package]) } it { is_expected.to match_array([conan_package, maven_package]) }
end end
context 'does not include packages without version number' do it_behaves_like 'concerning versionless param'
let_it_be(:package_without_version) { create(:maven_package, project: project, version: nil) }
it { is_expected.not_to include(package_without_version) }
end
end end
end end
...@@ -6,8 +6,9 @@ RSpec.describe API::GroupPackages do ...@@ -6,8 +6,9 @@ RSpec.describe API::GroupPackages do
let_it_be(:group) { create(:group, :public) } let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, namespace: group, name: 'project A') } let_it_be(:project) { create(:project, :public, namespace: group, name: 'project A') }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:params) { {} }
subject { get api(url) } subject { get api(url), params: params }
describe 'GET /groups/:id/packages' do describe 'GET /groups/:id/packages' do
let(:url) { "/groups/#{group.id}/packages" } let(:url) { "/groups/#{group.id}/packages" }
...@@ -142,6 +143,7 @@ RSpec.describe API::GroupPackages do ...@@ -142,6 +143,7 @@ RSpec.describe API::GroupPackages do
it_behaves_like 'returning response status', :bad_request it_behaves_like 'returning response status', :bad_request
end end
it_behaves_like 'with versionless packages'
it_behaves_like 'does not cause n^2 queries' it_behaves_like 'does not cause n^2 queries'
end end
end end
...@@ -11,12 +11,13 @@ RSpec.describe API::ProjectPackages do ...@@ -11,12 +11,13 @@ RSpec.describe API::ProjectPackages do
let!(:another_package) { create(:npm_package) } let!(:another_package) { create(:npm_package) }
let(:no_package_url) { "/projects/#{project.id}/packages/0" } let(:no_package_url) { "/projects/#{project.id}/packages/0" }
let(:wrong_package_url) { "/projects/#{project.id}/packages/#{another_package.id}" } let(:wrong_package_url) { "/projects/#{project.id}/packages/#{another_package.id}" }
let(:params) { {} }
describe 'GET /projects/:id/packages' do describe 'GET /projects/:id/packages' do
let(:url) { "/projects/#{project.id}/packages" } let(:url) { "/projects/#{project.id}/packages" }
let(:package_schema) { 'public_api/v4/packages/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 'without the need for a license' do
context 'project is public' do context 'project is public' do
...@@ -118,6 +119,7 @@ RSpec.describe API::ProjectPackages do ...@@ -118,6 +119,7 @@ RSpec.describe API::ProjectPackages do
end end
end end
it_behaves_like 'with versionless packages'
it_behaves_like 'does not cause n^2 queries' it_behaves_like 'does not cause n^2 queries'
end end
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 ...@@ -220,3 +220,45 @@ RSpec.shared_examples 'package workhorse uploads' do
end end
end 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