Commit 42d66675 authored by David Fernandez's avatar David Fernandez

Add Nuget API package content endpoints

See https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
Update finders to support a case insensitive mode
Add relevant presenter for package versions
parent 6bfe46fe
...@@ -4,14 +4,15 @@ module Packages ...@@ -4,14 +4,15 @@ module Packages
class PackageFinder class PackageFinder
MAX_PACKAGES_COUNT = 50 MAX_PACKAGES_COUNT = 50
def initialize(project, package_name:, package_version: nil) def initialize(project, package_name:, package_version: nil, limit: MAX_PACKAGES_COUNT)
@project = project @project = project
@package_name = package_name @package_name = package_name
@package_version = package_version @package_version = package_version
@limit = limit
end end
def execute def execute
packages.limit_recent(MAX_PACKAGES_COUNT) packages.limit_recent(@limit)
end end
private private
...@@ -19,7 +20,9 @@ module Packages ...@@ -19,7 +20,9 @@ module Packages
def packages def packages
result = @project.packages result = @project.packages
.nuget .nuget
.with_name(@package_name) .has_version
.processed
.with_name_like(@package_name)
result = result.with_version(@package_version) if @package_version.present? result = result.with_version(@package_version) if @package_version.present?
result result
end end
......
...@@ -28,7 +28,11 @@ class Packages::PackageFileFinder ...@@ -28,7 +28,11 @@ class Packages::PackageFileFinder
end end
def by_file_name(files) def by_file_name(files)
files.where(file_name: file_name) # rubocop: disable CodeReuse/ActiveRecord if params[:with_file_name_like]
files.with_file_name_like(file_name)
else
files.with_file_name(file_name)
end
end end
def by_conan_file_type(files) def by_conan_file_type(files)
......
...@@ -81,6 +81,10 @@ class Packages::Package < ApplicationRecord ...@@ -81,6 +81,10 @@ class Packages::Package < ApplicationRecord
pluck(:name) pluck(:name)
end end
def self.pluck_versions
pluck(:version)
end
def self.sort_by_attribute(method) def self.sort_by_attribute(method)
case method.to_s case method.to_s
when 'created_asc' then order_created when 'created_asc' then order_created
......
...@@ -19,6 +19,8 @@ class Packages::PackageFile < ApplicationRecord ...@@ -19,6 +19,8 @@ class Packages::PackageFile < ApplicationRecord
validates :file_name, presence: true validates :file_name, presence: true
scope :recent, -> { order(id: :desc) } scope :recent, -> { order(id: :desc) }
scope :with_file_name, ->(file_name) { where(file_name: file_name) }
scope :with_file_name_like, ->(file_name) { where(arel_table[:file_name].matches(file_name)) }
scope :with_files_stored_locally, -> { where(file_store: ::Packages::PackageFileUploader::Store::LOCAL) } scope :with_files_stored_locally, -> { where(file_store: ::Packages::PackageFileUploader::Store::LOCAL) }
scope :with_conan_file_metadata, -> { includes(:conan_file_metadatum) } scope :with_conan_file_metadata, -> { includes(:conan_file_metadatum) }
......
# frozen_string_literal: true
module Packages
module Nuget
class PackagesVersionsPresenter
attr_reader :packages
def initialize(packages)
@packages = packages
end
def versions
@packages.pluck_versions.sort
end
end
end
end
...@@ -56,6 +56,30 @@ module API ...@@ -56,6 +56,30 @@ module API
header(AUTHENTICATE_REALM_HEADER, AUTHENTICATE_REALM_NAME) header(AUTHENTICATE_REALM_HEADER, AUTHENTICATE_REALM_NAME)
unauthorized! unauthorized!
end end
def find_packages
packages = package_finder.execute
not_found!('Packages') unless packages.exists?
packages
end
def find_package
package = package_finder(package_version: params[:package_version]).execute
.first
not_found!('Package') unless package
package
end
def package_finder(finder_params = {})
::Packages::Nuget::PackageFinder.new(
authorized_user_project,
finder_params.merge(package_name: params[:package_name])
)
end
end end
before do before do
...@@ -132,12 +156,7 @@ module API ...@@ -132,12 +156,7 @@ module API
detail 'This feature was introduced in GitLab 12.8' detail 'This feature was introduced in GitLab 12.8'
end end
get 'index', format: :json do get 'index', format: :json do
packages = ::Packages::Nuget::PackageFinder.new(authorized_user_project, package_name: params[:package_name]) present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages),
.execute
not_found!('Packages') unless packages.exists?
present ::Packages::Nuget::PackagesMetadataPresenter.new(packages),
with: EE::API::Entities::Nuget::PackagesMetadata with: EE::API::Entities::Nuget::PackagesMetadata
end end
...@@ -148,32 +167,44 @@ module API ...@@ -148,32 +167,44 @@ module API
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
end end
get '*package_version', format: :json do get '*package_version', format: :json do
package = ::Packages::Nuget::PackageFinder present ::Packages::Nuget::PackageMetadataPresenter.new(find_package),
.new(authorized_user_project, package_name: params[:package_name], package_version: params[:package_version])
.execute
.first
not_found!('Package') unless package
present ::Packages::Nuget::PackageMetadataPresenter.new(package),
with: EE::API::Entities::Nuget::PackageMetadata with: EE::API::Entities::Nuget::PackageMetadata
end end
end end
# https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource # https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
desc 'The NuGet Content Service' do
detail 'This feature was introduced in GitLab 12.8'
end
params do params do
requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
end end
namespace '/download/*package_name/*package_version' do namespace '/download/*package_name' do
before do
authorize_read_package!(authorized_user_project)
end
desc 'The NuGet Content Service - index request' do
detail 'This feature was introduced in GitLab 12.8'
end
get 'index', format: :json do
present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages),
with: EE::API::Entities::Nuget::PackagesVersions
end
desc 'The NuGet Content Service - content request' do
detail 'This feature was introduced in GitLab 12.8'
end
params do params do
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
end end
get '*package_filename' do get '*package_version/*package_filename', format: :nupkg do
not_found!('package not found') # TODO NUGET API: not implemented yet. filename = "#{params[:package_filename]}.#{params[:format]}"
package_file = ::Packages::PackageFileFinder.new(find_package, filename, with_file_name_like: true)
.execute
not_found!('Package') unless package_file
# nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
present_carrierwave_file!(package_file.file, supports_direct_download: false)
end end
end end
end end
......
...@@ -907,6 +907,10 @@ module EE ...@@ -907,6 +907,10 @@ module EE
expose :count expose :count
expose :items, using: EE::API::Entities::Nuget::PackagesMetadataItem expose :items, using: EE::API::Entities::Nuget::PackagesMetadataItem
end end
class PackagesVersions < Grape::Entity
expose :versions
end
end end
class NpmPackage < Grape::Entity class NpmPackage < Grape::Entity
......
...@@ -42,7 +42,7 @@ FactoryBot.define do ...@@ -42,7 +42,7 @@ FactoryBot.define do
package_type { :nuget } package_type { :nuget }
after :create do |package| after :create do |package|
create :package_file, :nuget, package: package create :package_file, :nuget, package: package, file_name: "#{package.name}.#{package.version}.nupkg"
end end
end end
......
...@@ -3,16 +3,24 @@ require 'spec_helper' ...@@ -3,16 +3,24 @@ require 'spec_helper'
describe Packages::Nuget::PackageFinder do describe Packages::Nuget::PackageFinder do
let_it_be(:package1) { create(:nuget_package) } let_it_be(:package1) { create(:nuget_package) }
let_it_be(:package2) { create(:nuget_package, name: package1.name, version: '2.0.0', project: package1.project) }
let_it_be(:project) { package1.project } let_it_be(:project) { package1.project }
let_it_be(:package2) { create(:nuget_package, name: package1.name, version: '2.0.0', project: project) }
let_it_be(:package3) { create(:nuget_package, name: 'Another.Dummy.Package', project: project) }
let(:package_name) { package1.name } let(:package_name) { package1.name }
let(:package_version) { nil } let(:package_version) { nil }
let(:limit) { 50 }
describe '#execute!' do describe '#execute!' do
subject { described_class.new(project, package_name: package_name, package_version: package_version).execute } subject { described_class.new(project, package_name: package_name, package_version: package_version, limit: limit).execute }
it { is_expected.to match_array([package1, package2]) } it { is_expected.to match_array([package1, package2]) }
context 'with lower case package name' do
let(:package_name) { package1.name.downcase }
it { is_expected.to match_array([package1, package2]) }
end
context 'with unknown package name' do context 'with unknown package name' do
let(:package_name) { 'foobar' } let(:package_name) { 'foobar' }
...@@ -30,5 +38,38 @@ describe Packages::Nuget::PackageFinder do ...@@ -30,5 +38,38 @@ describe Packages::Nuget::PackageFinder do
it { is_expected.to be_empty } it { is_expected.to be_empty }
end end
context 'with limit hit' do
let_it_be(:package4) { create(:nuget_package, name: package1.name, project: project) }
let_it_be(:package5) { create(:nuget_package, name: package1.name, project: project) }
let_it_be(:package6) { create(:nuget_package, name: package1.name, project: project) }
let(:limit) { 2 }
it { is_expected.to match_array([package5, package6]) }
end
context 'with downcase package name' do
let(:package_name) { package1.name.downcase }
it { is_expected.to match_array([package1, package2]) }
end
context 'with prefix wildcard' do
let(:package_name) { "%#{package1.name[3..-1]}" }
it { is_expected.to match_array([package1, package2]) }
end
context 'with suffix wildcard' do
let(:package_name) { "#{package1.name[0..-3]}%" }
it { is_expected.to match_array([package1, package2]) }
end
context 'with surrounding wildcards' do
let(:package_name) { "%#{package1.name[3..-3]}%" }
it { is_expected.to match_array([package1, package2]) }
end
end end
end end
...@@ -2,32 +2,52 @@ ...@@ -2,32 +2,52 @@
require 'spec_helper' require 'spec_helper'
describe Packages::PackageFileFinder do describe Packages::PackageFileFinder do
let(:package) { create(:maven_package) } let_it_be(:package) { create(:maven_package) }
let(:package_file) { package.package_files.first } let_it_be(:package_file) { package.package_files.first }
let(:package_file_name) { package_file.file_name }
let(:params) { {} }
describe '#execute!' do RSpec.shared_examples 'package file finder examples' do
it 'returns a package file' do it { is_expected.to eq(package_file) }
finder = described_class.new(package, package_file.file_name)
context 'with conan_file_type' do
let_it_be(:package) { create(:conan_package) }
# conan packages contain a conanmanifest.txt file for both conan_file_types
let(:package_file_name) { 'conanmanifest.txt' }
let(:params) { { conan_file_type: :recipe_file } }
expect(finder.execute!).to eq(package_file) it { expect(subject.conan_file_type).to eq('recipe_file') }
end end
it 'raises an error' do context 'with file_name_like' do
finder = described_class.new(package, 'unknown.jpg') let(:package_file_name) { package_file.file_name.upcase }
let(:params) { { with_file_name_like: true } }
expect { finder.execute! }.to raise_error(ActiveRecord::RecordNotFound) it { is_expected.to eq(package_file) }
end end
end
context 'with conan_file_type' do describe '#execute' do
let(:package) { create(:conan_package) } subject { described_class.new(package, package_file_name, params).execute }
it_behaves_like 'package file finder examples'
context 'with unknown file_name' do
let(:package_file_name) { 'unknown.jpg' }
it { expect(subject).to be_nil }
end
end
describe '#execute!' do
subject { described_class.new(package, package_file_name, params).execute! }
it_behaves_like 'package file finder examples'
it 'returns a package of the correct file_type' do context 'with unknown file_name' do
# conan packages contain a conanmanifest.txt file for both conan_file_types let(:package_file_name) { 'unknown.jpg' }
result = described_class.new(package, 'conanmanifest.txt', conan_file_type: :recipe_file).execute!
expect(result.conan_file_type).to eq('recipe_file') it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
expect(result.conan_file_type).not_to eq('package_file')
end
end end
end end
end end
{
"type": "object",
"required": ["versions"],
"properties": {
"versions": {
"type": "array",
"items": { "type": "string" }
}
}
}
...@@ -11,6 +11,27 @@ RSpec.describe Packages::PackageFile, type: :model do ...@@ -11,6 +11,27 @@ RSpec.describe Packages::PackageFile, type: :model do
it { is_expected.to validate_presence_of(:package) } it { is_expected.to validate_presence_of(:package) }
end end
context 'with package filenames' do
let_it_be(:package_file1) { create(:package_file, :xml, file_name: 'FooBar') }
let_it_be(:package_file2) { create(:package_file, :xml, file_name: 'ThisIsATest') }
describe '.with_file_name' do
let(:filename) { 'FooBar' }
subject { described_class.with_file_name(filename) }
it { is_expected.to match_array([package_file1]) }
end
describe '.with_file_name_like' do
let(:filename) { 'foobar' }
subject { described_class.with_file_name_like(filename) }
it { is_expected.to match_array([package_file1]) }
end
end
it_behaves_like 'UpdateProjectStatistics' do it_behaves_like 'UpdateProjectStatistics' do
subject { build(:package_file, :jar, size: 42) } subject { build(:package_file, :jar, size: 42) }
end end
......
...@@ -165,4 +165,50 @@ RSpec.describe Packages::Package, type: :model do ...@@ -165,4 +165,50 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to match_array([package3, package2]) } it { is_expected.to match_array([package3, package2]) }
end end
context 'with several packages' do
let_it_be(:package1) { create(:nuget_package, name: 'FooBarish') }
let_it_be(:package2) { create(:npm_package) }
let_it_be(:package3) { create(:npm_package) }
describe '.pluck_names' do
subject { described_class.pluck_names.sort }
it { is_expected.to match_array([package1, package2, package3].map(&:name).sort) }
end
describe '.pluck_versions' do
subject { described_class.pluck_versions.sort }
it { is_expected.to match_array([package1, package2, package3].map(&:version).sort) }
end
describe '.with_name_like' do
subject { described_class.with_name_like(package_name) }
context 'with downcase name' do
let(:package_name) { 'foobarish' }
it { is_expected.to match_array([package1]) }
end
context 'with prefix wildcard' do
let(:package_name) { '%arish' }
it { is_expected.to match_array([package1]) }
end
context 'with suffix wildcard' do
let(:package_name) { 'foo%' }
it { is_expected.to match_array([package1]) }
end
context 'with surrounding wildcards' do
let(:package_name) { '%ooba%' }
it { is_expected.to match_array([package1]) }
end
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Packages::Nuget::PackagesVersionsPresenter do
let_it_be(:packages) { create_list(:nuget_package, 5) }
let_it_be(:presenter) { described_class.new(::Packages::Package.all) }
describe '#versions' do
subject { presenter.versions }
it { is_expected.to match_array(packages.map(&:version).sort) }
end
end
...@@ -230,7 +230,7 @@ describe API::NugetPackages do ...@@ -230,7 +230,7 @@ describe API::NugetPackages do
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/index' do describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/index' do
let_it_be(:package_name) { 'Dummy.Package' } let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:packages) { create_list(:nuget_package, 5, name: package_name, project: project) } let_it_be(:packages) { create_list(:nuget_package, 5, name: package_name, project: project) }
let_it_be(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.json" } let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.json" }
subject { get api(url) } subject { get api(url) }
...@@ -293,10 +293,10 @@ describe API::NugetPackages do ...@@ -293,10 +293,10 @@ describe API::NugetPackages do
it_behaves_like 'rejects nuget packages access with packages features disabled' it_behaves_like 'rejects nuget packages access with packages features disabled'
end end
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/:package_version' do describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/*package_version' do
let_it_be(:package_name) { 'Dummy.Package' } let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:package) { create(:nuget_package, name: 'Dummy.Package', project: project) } let_it_be(:package) { create(:nuget_package, name: 'Dummy.Package', project: project) }
let_it_be(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.json" } let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
subject { get api(url) } subject { get api(url) }
...@@ -351,6 +351,153 @@ describe API::NugetPackages do ...@@ -351,6 +351,153 @@ describe API::NugetPackages do
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end end
end end
context 'with invalid package name' do
let_it_be(:package_name) { 'Unkown' }
it_behaves_like 'rejects nuget packages access', :developer, :not_found
end
end
it_behaves_like 'rejects nuget packages access with feature flag disabled'
end
it_behaves_like 'rejects nuget packages access with packages features disabled'
end
describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/index' do
let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:packages) { create_list(:nuget_package, 5, name: package_name, project: project) }
let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package_name}/index.json" }
subject { get api(url) }
context 'with packages features enabled' do
before do
stub_licensed_features(packages: true)
end
context 'with feature flag enabled' do
before do
stub_feature_flags(nuget_package_registry: { enabled: true, thing: project })
end
context 'with valid project' do
using RSpec::Parameterized::TableSyntax
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :developer | true | true | 'process nuget download versions request' | :success
'PUBLIC' | :guest | true | true | 'process nuget download versions request' | :success
'PUBLIC' | :developer | true | false | 'process nuget download versions request' | :success
'PUBLIC' | :guest | true | false | 'process nuget download versions request' | :success
'PUBLIC' | :developer | false | true | 'process nuget download versions request' | :success
'PUBLIC' | :guest | false | true | 'process nuget download versions request' | :success
'PUBLIC' | :developer | false | false | 'process nuget download versions request' | :success
'PUBLIC' | :guest | false | false | 'process nuget download versions request' | :success
'PUBLIC' | :anonymous | false | true | 'process nuget download versions request' | :success
'PRIVATE' | :developer | true | true | 'process nuget download versions request' | :success
'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
end
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
subject { get api(url), headers: headers }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
end
after do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
it_behaves_like 'rejects nuget access with unknown project id'
it_behaves_like 'rejects nuget access with invalid project id'
end
it_behaves_like 'rejects nuget packages access with feature flag disabled'
end
it_behaves_like 'rejects nuget packages access with packages features disabled'
end
describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename' do
let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:package) { create(:nuget_package, project: project, name: package_name) }
let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.nupkg" }
subject { get api(url) }
context 'with packages features enabled' do
before do
stub_licensed_features(packages: true)
end
context 'with feature flag enabled' do
before do
stub_feature_flags(nuget_package_registry: { enabled: true, thing: project })
end
context 'with valid project' do
using RSpec::Parameterized::TableSyntax
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :developer | true | true | 'process nuget download content request' | :success
'PUBLIC' | :guest | true | true | 'process nuget download content request' | :success
'PUBLIC' | :developer | true | false | 'process nuget download content request' | :success
'PUBLIC' | :guest | true | false | 'process nuget download content request' | :success
'PUBLIC' | :developer | false | true | 'process nuget download content request' | :success
'PUBLIC' | :guest | false | true | 'process nuget download content request' | :success
'PUBLIC' | :developer | false | false | 'process nuget download content request' | :success
'PUBLIC' | :guest | false | false | 'process nuget download content request' | :success
'PUBLIC' | :anonymous | false | true | 'process nuget download content request' | :success
'PRIVATE' | :developer | true | true | 'process nuget download content request' | :success
'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
end
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
subject { get api(url), headers: headers }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
end
after do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
it_behaves_like 'rejects nuget access with unknown project id'
it_behaves_like 'rejects nuget access with invalid project id'
end end
it_behaves_like 'rejects nuget packages access with feature flag disabled' it_behaves_like 'rejects nuget packages access with feature flag disabled'
......
...@@ -50,13 +50,8 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu ...@@ -50,13 +50,8 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu
subject subject
expect(response.content_type.to_s).to eq('application/json') expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to be_a(Hash)
end
it 'returns a valid nuget service index json' do
subject
expect(json_response).to match_schema('public_api/v4/packages/nuget/service_index', dir: 'ee') expect(json_response).to match_schema('public_api/v4/packages/nuget/service_index', dir: 'ee')
expect(json_response).to be_a(Hash)
end end
context 'with invalid format' do context 'with invalid format' do
...@@ -67,6 +62,16 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu ...@@ -67,6 +62,16 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu
end end
end end
RSpec.shared_examples 'returning nuget metadata json response with json schema' do |json_schema|
it 'returns a valid json response' do
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to match_schema(json_schema, dir: 'ee')
expect(json_response).to be_a(Hash)
end
end
RSpec.shared_examples 'process nuget metadata request at package name level' do |user_type, status, add_member = true| RSpec.shared_examples 'process nuget metadata request at package name level' do |user_type, status, add_member = true|
context "for user type #{user_type}" do context "for user type #{user_type}" do
before do before do
...@@ -75,24 +80,21 @@ RSpec.shared_examples 'process nuget metadata request at package name level' do ...@@ -75,24 +80,21 @@ RSpec.shared_examples 'process nuget metadata request at package name level' do
it_behaves_like 'returning response status', status it_behaves_like 'returning response status', status
it 'returns a valid json response' do it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/packages_metadata'
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to be_a(Hash)
end
it 'returns a valid nuget packages metadata json' do
subject
expect(json_response).to match_schema('public_api/v4/packages/nuget/packages_metadata', dir: 'ee')
end
context 'with invalid format' do context 'with invalid format' do
let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.xls" } let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.xls" }
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end end
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
it_behaves_like 'returning response status', status
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/packages_metadata'
end
end end
end end
...@@ -104,24 +106,21 @@ RSpec.shared_examples 'process nuget metadata request at package name and packag ...@@ -104,24 +106,21 @@ RSpec.shared_examples 'process nuget metadata request at package name and packag
it_behaves_like 'returning response status', status it_behaves_like 'returning response status', status
it 'returns a valid json response' do it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/package_metadata'
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to be_a(Hash)
end
it 'returns a valid nuget package metadata json' do
subject
expect(json_response).to match_schema('public_api/v4/packages/nuget/package_metadata', dir: 'ee')
end
context 'with invalid format' do context 'with invalid format' do
let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.xls" } let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.xls" }
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end end
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
it_behaves_like 'returning response status', status
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/package_metadata'
end
end end
end end
...@@ -224,6 +223,77 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member = ...@@ -224,6 +223,77 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
end end
end end
RSpec.shared_examples 'process nuget download versions request' do |user_type, status, add_member = true|
RSpec.shared_examples 'returns a valid nuget download versions json response' do
it 'returns a valid json response' do
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to match_schema('public_api/v4/packages/nuget/download_versions', dir: 'ee')
expect(json_response).to be_a(Hash)
expect(json_response['versions']).to match_array(packages.map(&:version).sort)
end
end
context "for user type #{user_type}" do
before do
project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
end
it_behaves_like 'returning response status', status
it_behaves_like 'returns a valid nuget download versions json response'
context 'with invalid format' do
let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package_name}/index.xls" }
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
it_behaves_like 'returning response status', status
it_behaves_like 'returns a valid nuget download versions json response'
end
end
end
RSpec.shared_examples 'process nuget download content request' do |user_type, status, add_member = true|
context "for user type #{user_type}" do
before do
project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
end
it_behaves_like 'returning response status', status
it 'returns a valid package archive' do
subject
expect(response.content_type.to_s).to eq('application/octet-stream')
end
context 'with invalid format' do
let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.xls" }
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
it_behaves_like 'returning response status', status
it 'returns a valid package archive' do
subject
expect(response.content_type.to_s).to eq('application/octet-stream')
end
end
end
end
RSpec.shared_examples 'rejects nuget access with invalid project id' do RSpec.shared_examples 'rejects nuget access with invalid project id' do
context 'with a project id with invalid integers' do context 'with a project id with invalid integers' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
......
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