Commit b8c5c14d authored by David Fernandez's avatar David Fernandez

Extract nuget metadata and persist it

Extract:
  * project url
  * license url
  * icon url
and persist them using `Packages::NugetMetadtum`.

Expose them in the search and metadata endpoints.
parent d8ed2944
# frozen_string_literal: true
class CreatePackagesNugetMetadata < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
LICENSE_URL_CONSTRAINT_NAME = 'packages_nuget_metadata_license_url_constraint'
PROJECT_URL_CONSTRAINT_NAME = 'packages_nuget_metadata_project_url_constraint'
ICON_URL_CONSTRAINT_NAME = 'packages_nuget_metadata_icon_url_constraint'
def up
unless table_exists?(:packages_nuget_metadata)
with_lock_retries do
create_table :packages_nuget_metadata, id: false do |t|
t.references :package, primary_key: true, default: nil, index: false, foreign_key: { to_table: :packages_packages, on_delete: :cascade }, type: :bigint
t.text :license_url
t.text :project_url
t.text :icon_url
end
end
end
add_text_limit :packages_nuget_metadata, :license_url, 255, constraint_name: LICENSE_URL_CONSTRAINT_NAME
add_text_limit :packages_nuget_metadata, :project_url, 255, constraint_name: PROJECT_URL_CONSTRAINT_NAME
add_text_limit :packages_nuget_metadata, :icon_url, 255, constraint_name: ICON_URL_CONSTRAINT_NAME
end
def down
drop_table :packages_nuget_metadata
end
end
...@@ -4654,6 +4654,16 @@ CREATE TABLE public.packages_nuget_dependency_link_metadata ( ...@@ -4654,6 +4654,16 @@ CREATE TABLE public.packages_nuget_dependency_link_metadata (
CONSTRAINT packages_nuget_dependency_link_metadata_target_framework_constr CHECK ((char_length(target_framework) <= 255)) CONSTRAINT packages_nuget_dependency_link_metadata_target_framework_constr CHECK ((char_length(target_framework) <= 255))
); );
CREATE TABLE public.packages_nuget_metadata (
package_id bigint NOT NULL,
license_url text,
project_url text,
icon_url text,
CONSTRAINT packages_nuget_metadata_icon_url_constraint CHECK ((char_length(icon_url) <= 255)),
CONSTRAINT packages_nuget_metadata_license_url_constraint CHECK ((char_length(license_url) <= 255)),
CONSTRAINT packages_nuget_metadata_project_url_constraint CHECK ((char_length(project_url) <= 255))
);
CREATE TABLE public.packages_package_files ( CREATE TABLE public.packages_package_files (
id bigint NOT NULL, id bigint NOT NULL,
package_id bigint NOT NULL, package_id bigint NOT NULL,
...@@ -8535,6 +8545,9 @@ ALTER TABLE ONLY public.packages_maven_metadata ...@@ -8535,6 +8545,9 @@ ALTER TABLE ONLY public.packages_maven_metadata
ALTER TABLE ONLY public.packages_nuget_dependency_link_metadata ALTER TABLE ONLY public.packages_nuget_dependency_link_metadata
ADD CONSTRAINT packages_nuget_dependency_link_metadata_pkey PRIMARY KEY (dependency_link_id); ADD CONSTRAINT packages_nuget_dependency_link_metadata_pkey PRIMARY KEY (dependency_link_id);
ALTER TABLE ONLY public.packages_nuget_metadata
ADD CONSTRAINT packages_nuget_metadata_pkey PRIMARY KEY (package_id);
ALTER TABLE ONLY public.packages_package_files ALTER TABLE ONLY public.packages_package_files
ADD CONSTRAINT packages_package_files_pkey PRIMARY KEY (id); ADD CONSTRAINT packages_package_files_pkey PRIMARY KEY (id);
...@@ -12598,6 +12611,9 @@ ALTER TABLE ONLY public.serverless_domain_cluster ...@@ -12598,6 +12611,9 @@ ALTER TABLE ONLY public.serverless_domain_cluster
ALTER TABLE ONLY public.ci_job_variables ALTER TABLE ONLY public.ci_job_variables
ADD CONSTRAINT fk_rails_fbf3b34792 FOREIGN KEY (job_id) REFERENCES public.ci_builds(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_fbf3b34792 FOREIGN KEY (job_id) REFERENCES public.ci_builds(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.packages_nuget_metadata
ADD CONSTRAINT fk_rails_fc0c19f5b4 FOREIGN KEY (package_id) REFERENCES public.packages_packages(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.cluster_groups ALTER TABLE ONLY public.cluster_groups
ADD CONSTRAINT fk_rails_fdb8648a96 FOREIGN KEY (cluster_id) REFERENCES public.clusters(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_fdb8648a96 FOREIGN KEY (cluster_id) REFERENCES public.clusters(id) ON DELETE CASCADE;
...@@ -13801,6 +13817,7 @@ COPY "schema_migrations" (version) FROM STDIN; ...@@ -13801,6 +13817,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200429181955 20200429181955
20200429182245 20200429182245
20200430103158 20200430103158
20200430130048
20200505164958 20200505164958
20200505171834 20200505171834
20200505172405 20200505172405
......
# frozen_string_literal: true
class Packages::Nuget::Metadatum < ApplicationRecord
belongs_to :package, -> { where(package_type: :nuget) }, inverse_of: :nuget_metadatum
validates :package, presence: true
validates :license_url, public_url: { allow_blank: true }
validates :project_url, public_url: { allow_blank: true }
validates :icon_url, public_url: { allow_blank: true }
validate :ensure_at_least_one_field_supplied
validate :ensure_nuget_package_type
private
def ensure_at_least_one_field_supplied
return if license_url? || project_url? || icon_url?
errors.add(:base, _('Nuget metadatum must have at least license_url, project_url or icon_url set'))
end
def ensure_nuget_package_type
return if package&.nuget?
errors.add(:base, _('Package type must be NuGet'))
end
end
...@@ -12,6 +12,7 @@ class Packages::Package < ApplicationRecord ...@@ -12,6 +12,7 @@ class Packages::Package < ApplicationRecord
has_one :conan_metadatum, inverse_of: :package, class_name: 'Packages::Conan::Metadatum' has_one :conan_metadatum, inverse_of: :package, class_name: 'Packages::Conan::Metadatum'
has_one :pypi_metadatum, inverse_of: :package, class_name: 'Packages::Pypi::Metadatum' has_one :pypi_metadatum, inverse_of: :package, class_name: 'Packages::Pypi::Metadatum'
has_one :maven_metadatum, inverse_of: :package, class_name: 'Packages::Maven::Metadatum' has_one :maven_metadatum, inverse_of: :package, class_name: 'Packages::Maven::Metadatum'
has_one :nuget_metadatum, inverse_of: :package, class_name: 'Packages::Nuget::Metadatum'
has_one :build_info, inverse_of: :package has_one :build_info, inverse_of: :package
accepts_nested_attributes_for :conan_metadatum accepts_nested_attributes_for :conan_metadatum
......
...@@ -47,10 +47,19 @@ module Packages ...@@ -47,10 +47,19 @@ module Packages
package_version: package.version, package_version: package.version,
archive_url: archive_url_for(package), archive_url: archive_url_for(package),
summary: BLANK_STRING, summary: BLANK_STRING,
tags: tags_for(package) tags: tags_for(package),
metadatum: metadatum_for(package)
} }
end end
def metadatum_for(package)
metadatum = package.nuget_metadatum
return {} unless metadatum
metadatum.slice(:project_url, :license_url, :icon_url)
.compact
end
def base_path_for(package) def base_path_for(package)
api_v4_projects_packages_nuget_path(id: package.project_id) api_v4_projects_packages_nuget_path(id: package.project_id)
end end
......
...@@ -28,7 +28,8 @@ module Packages ...@@ -28,7 +28,8 @@ module Packages
summary: '', summary: '',
total_downloads: 0, total_downloads: 0,
verified: true, verified: true,
tags: tags_for(latest_package) tags: tags_for(latest_package),
metadatum: metadatum_for(latest_package)
} }
end end
end end
......
...@@ -9,7 +9,10 @@ module Packages ...@@ -9,7 +9,10 @@ module Packages
XPATHS = { XPATHS = {
package_name: '//xmlns:package/xmlns:metadata/xmlns:id', package_name: '//xmlns:package/xmlns:metadata/xmlns:id',
package_version: '//xmlns:package/xmlns:metadata/xmlns:version' package_version: '//xmlns:package/xmlns:metadata/xmlns:version',
license_url: '//xmlns:package/xmlns:metadata/xmlns:licenseUrl',
project_url: '//xmlns:package/xmlns:metadata/xmlns:projectUrl',
icon_url: '//xmlns:package/xmlns:metadata/xmlns:iconUrl'
}.freeze }.freeze
XPATH_DEPENDENCIES = '//xmlns:package/xmlns:metadata/xmlns:dependencies/xmlns:dependency' XPATH_DEPENDENCIES = '//xmlns:package/xmlns:metadata/xmlns:dependencies/xmlns:dependency'
...@@ -45,8 +48,9 @@ module Packages ...@@ -45,8 +48,9 @@ module Packages
def extract_metadata(file) def extract_metadata(file)
doc = Nokogiri::XML(file) doc = Nokogiri::XML(file)
XPATHS.map { |key, query| [key, doc.xpath(query).text] } XPATHS.map { |key, query| [key, doc.xpath(query).text.presence] }
.to_h .to_h
.compact
.tap do |metadata| .tap do |metadata|
metadata[:package_dependencies] = extract_dependencies(doc) metadata[:package_dependencies] = extract_dependencies(doc)
metadata[:package_tags] = extract_tags(doc) metadata[:package_tags] = extract_tags(doc)
......
# frozen_string_literal: true
module Packages
module Nuget
class SyncMetadatumService
include Gitlab::Utils::StrongMemoize
def initialize(package, metadata)
@package = package
@metadata = metadata
end
def execute
if blank_metadata?
metadatum.destroy! if metadatum.persisted?
else
metadatum.update!(
license_url: license_url,
project_url: project_url,
icon_url: icon_url
)
end
end
private
def metadatum
strong_memoize(:metadatum) do
@package.nuget_metadatum || @package.build_nuget_metadatum
end
end
def blank_metadata?
project_url.blank? && license_url.blank? && icon_url.blank?
end
def project_url
@metadata[:project_url]
end
def license_url
@metadata[:license_url]
end
def icon_url
@metadata[:icon_url]
end
end
end
end
...@@ -37,8 +37,14 @@ module Packages ...@@ -37,8 +37,14 @@ module Packages
private private
def update_package(package) def update_package(package)
::Packages::UpdateTagsService.new(package, package_tags) ::Packages::Nuget::SyncMetadatumService
.new(package, metadata.slice(:project_url, :license_url, :icon_url))
.execute .execute
::Packages::UpdateTagsService
.new(package, package_tags)
.execute
rescue => e
raise InvalidMetadataError, e.message
end end
def valid_metadata? def valid_metadata?
......
---
title: Add Nuget metadatum support in GitLab Packages
merge_request: 30994
author:
type: added
# frozen_string_literal: true
module EE
module API
module Entities
module Nuget
class Metadatum < Grape::Entity
expose :project_url, as: :projectUrl, expose_nil: false
expose :license_url, as: :licenseUrl, expose_nil: false
expose :icon_url, as: :iconUrl, expose_nil: false
end
end
end
end
end
...@@ -13,6 +13,7 @@ module EE ...@@ -13,6 +13,7 @@ module EE
expose :tags expose :tags
expose :archive_url, as: :packageContent expose :archive_url, as: :packageContent
expose :summary expose :summary
expose :metadatum, using: EE::API::Entities::Nuget::Metadatum, merge: true
end end
end end
end end
......
...@@ -15,6 +15,7 @@ module EE ...@@ -15,6 +15,7 @@ module EE
expose :version expose :version
expose :versions, using: EE::API::Entities::Nuget::SearchResultVersion expose :versions, using: EE::API::Entities::Nuget::SearchResultVersion
expose :tags expose :tags
expose :metadatum, using: EE::API::Entities::Nuget::Metadatum, merge: true
end end
end end
end end
......
...@@ -47,6 +47,12 @@ FactoryBot.define do ...@@ -47,6 +47,12 @@ FactoryBot.define do
after :create do |package| after :create do |package|
create :package_file, :nuget, package: package, file_name: "#{package.name}.#{package.version}.nupkg" create :package_file, :nuget, package: package, file_name: "#{package.name}.#{package.version}.nupkg"
end end
trait(:with_metadatum) do
after :build do |pkg|
pkg.nuget_metadatum = build(:nuget_metadatum)
end
end
end end
factory :pypi_package do factory :pypi_package do
...@@ -242,6 +248,14 @@ FactoryBot.define do ...@@ -242,6 +248,14 @@ FactoryBot.define do
required_python { '>=2.7' } required_python { '>=2.7' }
end end
factory :nuget_metadatum, class: 'Packages::Nuget::Metadatum' do
package { create(:nuget_package) }
license_url { 'http://www.gitlab.com' }
project_url { 'http://www.gitlab.com' }
icon_url { 'http://www.gitlab.com' }
end
factory :conan_file_metadatum, class: 'Packages::Conan::FileMetadatum' do factory :conan_file_metadatum, class: 'Packages::Conan::FileMetadatum' do
package_file package_file
recipe_revision { '0' } recipe_revision { '0' }
......
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
"packageContent": { "type": "string" }, "packageContent": { "type": "string" },
"summary": { "const": "" }, "summary": { "const": "" },
"tags": { "type": "string" }, "tags": { "type": "string" },
"projectUrl": { "type": "string" },
"licenseUrl": { "type": "string" },
"iconUrl": { "type": "string" },
"version": { "type": "string" } "version": { "type": "string" }
} }
} }
......
...@@ -31,6 +31,9 @@ ...@@ -31,6 +31,9 @@
"packageContent": { "type": "string" }, "packageContent": { "type": "string" },
"summary": { "const": "" }, "summary": { "const": "" },
"tags": { "type": "string" }, "tags": { "type": "string" },
"projectUrl": { "type": "string" },
"licenseUrl": { "type": "string" },
"iconUrl": { "type": "string" },
"version": { "type": "string" } "version": { "type": "string" }
} }
} }
......
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
"totalDownloads": { "const": 0 }, "totalDownloads": { "const": 0 },
"verified": { "const": true }, "verified": { "const": true },
"tags": { "type": "string" }, "tags": { "type": "string" },
"projectUrl": { "type": "string" },
"licenseUrl": { "type": "string" },
"iconUrl": { "type": "string" },
"versions": { "versions": {
"type": "array", "type": "array",
"items": { "items": {
......
# frozen_string_literal: true
require 'spec_helper'
describe EE::API::Entities::Nuget::Metadatum do
let(:metadatum) do
{
project_url: 'http://sandbox.com/project',
license_url: 'http://sandbox.com/license',
icon_url: 'http://sandbox.com/icon'
}
end
let(:expected) do
{
'projectUrl': 'http://sandbox.com/project',
'licenseUrl': 'http://sandbox.com/license',
'iconUrl': 'http://sandbox.com/icon'
}
end
let(:entity) { described_class.new(metadatum) }
subject { entity.as_json }
it { is_expected.to eq(expected) }
%i[project_url license_url icon_url].each do |optional_field|
context "metadatum without #{optional_field}" do
let(:metadatum_without_a_field) { metadatum.except(optional_field) }
let(:expected_without_a_field) { expected.except(optional_field.to_s.camelize(:lower).to_sym) }
let(:entity) { described_class.new(metadatum_without_a_field) }
it { is_expected.to eq(expected_without_a_field) }
end
end
end
...@@ -12,7 +12,12 @@ describe EE::API::Entities::Nuget::PackageMetadataCatalogEntry do ...@@ -12,7 +12,12 @@ describe EE::API::Entities::Nuget::PackageMetadataCatalogEntry do
package_version: '1.2.3', package_version: '1.2.3',
tags: 'tag1 tag2 tag3', tags: 'tag1 tag2 tag3',
archive_url: 'http://sandbox.com/archive/package', archive_url: 'http://sandbox.com/archive/package',
summary: 'Summary' summary: 'Summary',
metadatum: {
project_url: 'http://sandbox.com/project',
license_url: 'http://sandbox.com/license',
icon_url: 'http://sandbox.com/icon'
}
} }
end end
...@@ -25,7 +30,10 @@ describe EE::API::Entities::Nuget::PackageMetadataCatalogEntry do ...@@ -25,7 +30,10 @@ describe EE::API::Entities::Nuget::PackageMetadataCatalogEntry do
'dependencyGroups': [], 'dependencyGroups': [],
'tags': 'tag1 tag2 tag3', 'tags': 'tag1 tag2 tag3',
'packageContent': 'http://sandbox.com/archive/package', 'packageContent': 'http://sandbox.com/archive/package',
'summary': 'Summary' 'summary': 'Summary',
'projectUrl': 'http://sandbox.com/project',
'licenseUrl': 'http://sandbox.com/license',
'iconUrl': 'http://sandbox.com/icon'
} }
end end
......
...@@ -19,10 +19,14 @@ describe EE::API::Entities::Nuget::SearchResult do ...@@ -19,10 +19,14 @@ describe EE::API::Entities::Nuget::SearchResult do
summary: 'Summary', summary: 'Summary',
total_downloads: 100, total_downloads: 100,
verified: true, verified: true,
tags: 'tag1 tag2 tag3' tags: 'tag1 tag2 tag3',
metadatum: {
project_url: 'http://sandbox.com/project',
license_url: 'http://sandbox.com/license',
icon_url: 'http://sandbox.com/icon'
}
} }
end end
let(:expected) do let(:expected) do
{ {
'@type': 'Package', '@type': 'Package',
...@@ -34,6 +38,9 @@ describe EE::API::Entities::Nuget::SearchResult do ...@@ -34,6 +38,9 @@ describe EE::API::Entities::Nuget::SearchResult do
'verified': true, 'verified': true,
'version': '1.2.3', 'version': '1.2.3',
'tags': 'tag1 tag2 tag3', 'tags': 'tag1 tag2 tag3',
'projectUrl': 'http://sandbox.com/project',
'licenseUrl': 'http://sandbox.com/license',
'iconUrl': 'http://sandbox.com/icon',
'versions': [ 'versions': [
{ {
'@id': 'http://sandbox.com/json/package', '@id': 'http://sandbox.com/json/package',
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Packages::Nuget::Metadatum, type: :model do
describe 'relationships' do
it { is_expected.to belong_to(:package).inverse_of(:nuget_metadatum) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:package) }
%i[license_url project_url icon_url].each do |url|
describe "##{url}" do
it { is_expected.to allow_value('http://sandbox.com').for(url) }
it { is_expected.to allow_value('https://sandbox.com').for(url) }
it { is_expected.not_to allow_value('123').for(url) }
it { is_expected.not_to allow_value('sandbox.com').for(url) }
end
describe '#ensure_at_least_one_field_supplied' do
subject { build(:nuget_metadatum) }
it 'rejects unfilled metadatum' do
subject.attributes = { license_url: nil, project_url: nil, icon_url: nil }
expect(subject).not_to be_valid
expect(subject.errors).to contain_exactly('Nuget metadatum must have at least license_url, project_url or icon_url set')
end
end
describe '#ensure_nuget_package_type' do
subject { build(:nuget_metadatum) }
it 'rejects if not linked to a nuget package' do
subject.package = build(:npm_package)
expect(subject).not_to be_valid
expect(subject.errors).to contain_exactly('Package type must be NuGet')
end
end
end
end
end
...@@ -11,6 +11,7 @@ RSpec.describe Packages::Package, type: :model do ...@@ -11,6 +11,7 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to have_many(:tags).inverse_of(:package) } it { is_expected.to have_many(:tags).inverse_of(:package) }
it { is_expected.to have_one(:conan_metadatum).inverse_of(:package) } it { is_expected.to have_one(:conan_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:maven_metadatum).inverse_of(:package) } it { is_expected.to have_one(:maven_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:nuget_metadatum).inverse_of(:package) }
end end
describe '.sort_by_attribute' do describe '.sort_by_attribute' do
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
describe Packages::Nuget::PackageMetadataPresenter do describe Packages::Nuget::PackageMetadataPresenter do
let_it_be(:package) { create(:nuget_package) } let_it_be(:package) { create(:nuget_package, :with_metadatum) }
let_it_be(:tag1) { create(:packages_tag, name: 'tag1', package: package) } let_it_be(:tag1) { create(:packages_tag, name: 'tag1', package: package) }
let_it_be(:tag2) { create(:packages_tag, name: 'tag2', package: package) } let_it_be(:tag2) { create(:packages_tag, name: 'tag2', package: package) }
let_it_be(:presenter) { described_class.new(package) } let_it_be(:presenter) { described_class.new(package) }
...@@ -37,6 +37,10 @@ describe Packages::Nuget::PackageMetadataPresenter do ...@@ -37,6 +37,10 @@ describe Packages::Nuget::PackageMetadataPresenter do
expect(entry[:package_name]).to eq package.name expect(entry[:package_name]).to eq package.name
expect(entry[:package_version]).to eq package.version expect(entry[:package_version]).to eq package.version
expect(entry[:tags].split(::Packages::Tag::NUGET_TAGS_SEPARATOR)).to contain_exactly('tag1', 'tag2') expect(entry[:tags].split(::Packages::Tag::NUGET_TAGS_SEPARATOR)).to contain_exactly('tag1', 'tag2')
%i[project_url license_url icon_url].each do |field|
expect(entry.dig(:metadatum, field)).to eq(package.nuget_metadatum.send(field))
end
end end
end end
end end
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
describe Packages::Nuget::PackagesMetadataPresenter do describe Packages::Nuget::PackagesMetadataPresenter do
let_it_be(:packages) { create_list(:nuget_package, 5, name: 'Dummy.Package') } let_it_be(:packages) { create_list(:nuget_package, 5, :with_metadatum, name: 'Dummy.Package') }
let_it_be(:presenter) { described_class.new(packages) } let_it_be(:presenter) { described_class.new(packages) }
describe '#count' do describe '#count' do
...@@ -51,6 +51,10 @@ describe Packages::Nuget::PackagesMetadataPresenter do ...@@ -51,6 +51,10 @@ describe Packages::Nuget::PackagesMetadataPresenter do
%i[authors summary].each { |field| expect(catalog_entry[field]).to be_blank } %i[authors summary].each { |field| expect(catalog_entry[field]).to be_blank }
expect(catalog_entry[:dependencies]).to eq [] expect(catalog_entry[:dependencies]).to eq []
expect(catalog_entry[:tags].split(::Packages::Tag::NUGET_TAGS_SEPARATOR)).to contain_exactly('tag1', 'tag2') expect(catalog_entry[:tags].split(::Packages::Tag::NUGET_TAGS_SEPARATOR)).to contain_exactly('tag1', 'tag2')
%i[project_url license_url icon_url].each do |field|
expect(catalog_entry.dig(:metadatum, field)).not_to be_blank
end
end end
end end
end end
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe Packages::Nuget::SearchResultsPresenter do describe Packages::Nuget::SearchResultsPresenter do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:package_a) { create(:nuget_package, project: project, name: 'DummyPackageA') } let_it_be(:package_a) { create(:nuget_package, :with_metadatum, project: project, name: 'DummyPackageA') }
let_it_be(:tag1) { create(:packages_tag, package: package_a, name: 'tag1') } let_it_be(:tag1) { create(:packages_tag, package: package_a, name: 'tag1') }
let_it_be(:tag2) { create(:packages_tag, package: package_a, name: 'tag2') } let_it_be(:tag2) { create(:packages_tag, package: package_a, name: 'tag2') }
let_it_be(:packages_b) { create_list(:nuget_package, 5, project: project, name: 'DummyPackageB') } let_it_be(:packages_b) { create_list(:nuget_package, 5, project: project, name: 'DummyPackageB') }
...@@ -24,13 +24,13 @@ describe Packages::Nuget::SearchResultsPresenter do ...@@ -24,13 +24,13 @@ describe Packages::Nuget::SearchResultsPresenter do
it 'returns the proper data structure' do it 'returns the proper data structure' do
expect(data.size).to eq 3 expect(data.size).to eq 3
pkg_a, pkg_b, pkg_c = data pkg_a, pkg_b, pkg_c = data
expect_package_result(pkg_a, package_a.name, [package_a.version], %w(tag1 tag2)) expect_package_result(pkg_a, package_a.name, [package_a.version], %w(tag1 tag2), with_metadatum: true)
expect_package_result(pkg_b, packages_b.first.name, packages_b.map(&:version)) expect_package_result(pkg_b, packages_b.first.name, packages_b.map(&:version))
expect_package_result(pkg_c, packages_c.first.name, packages_c.map(&:version)) expect_package_result(pkg_c, packages_c.first.name, packages_c.map(&:version))
end end
# rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/AbcSize
def expect_package_result(package_json, name, versions, tags = []) def expect_package_result(package_json, name, versions, tags = [], with_metadatum: false)
expect(package_json[:type]).to eq 'Package' expect(package_json[:type]).to eq 'Package'
expect(package_json[:authors]).to be_blank expect(package_json[:authors]).to be_blank
expect(package_json[:name]).to eq(name) expect(package_json[:name]).to eq(name)
...@@ -49,6 +49,10 @@ describe Packages::Nuget::SearchResultsPresenter do ...@@ -49,6 +49,10 @@ describe Packages::Nuget::SearchResultsPresenter do
else else
expect(package_json[:tags]).to be_blank expect(package_json[:tags]).to be_blank
end end
%i[project_url license_url icon_url].each do |field|
expect(package_json.dig(:metadatum, field)).to with_metadatum ? be_present : be_blank
end
end end
# rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/AbcSize
end end
......
...@@ -204,7 +204,7 @@ describe API::NugetPackages do ...@@ -204,7 +204,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, :with_metadatum, name: package_name, project: project) }
let_it_be(:tags) { packages.each { |pkg| create(:packages_tag, package: pkg, name: 'test') } } let_it_be(:tags) { packages.each { |pkg| create(:packages_tag, package: pkg, name: 'test') } }
let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.json" } let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.json" }
...@@ -265,7 +265,7 @@ describe API::NugetPackages do ...@@ -265,7 +265,7 @@ describe API::NugetPackages do
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, :with_metadatum, name: 'Dummy.Package', project: project) }
let_it_be(:tag) { create(:packages_tag, package: package, name: 'test') } let_it_be(:tag) { create(:packages_tag, package: package, name: 'test') }
let(: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" }
...@@ -448,7 +448,7 @@ describe API::NugetPackages do ...@@ -448,7 +448,7 @@ describe API::NugetPackages do
end end
describe 'GET /api/v4/projects/:id/packages/nuget/query' do describe 'GET /api/v4/projects/:id/packages/nuget/query' do
let_it_be(:package_a) { create(:nuget_package, name: 'Dummy.PackageA', project: project) } let_it_be(:package_a) { create(:nuget_package, :with_metadatum, name: 'Dummy.PackageA', project: project) }
let_it_be(:tag) { create(:packages_tag, package: package_a, name: 'test') } let_it_be(:tag) { create(:packages_tag, package: package_a, name: 'test') }
let_it_be(:packages_b) { create_list(:nuget_package, 5, name: 'Dummy.PackageB', project: project) } let_it_be(:packages_b) { create_list(:nuget_package, 5, name: 'Dummy.PackageB', project: project) }
let_it_be(:packages_c) { create_list(:nuget_package, 5, name: 'Dummy.PackageC', project: project) } let_it_be(:packages_c) { create_list(:nuget_package, 5, name: 'Dummy.PackageC', project: project) }
......
...@@ -53,6 +53,18 @@ describe Packages::Nuget::MetadataExtractionService do ...@@ -53,6 +53,18 @@ describe Packages::Nuget::MetadataExtractionService do
end end
end end
context 'with a nuspec file with metadata' do
let_it_be(:nuspec_filepath) { 'nuget/with_metadata.nuspec' }
before do
allow(service).to receive(:nuspec_file).and_return(fixture_file(nuspec_filepath, dir: 'ee'))
end
it { expect(subject[:license_url]).to eq('https://opensource.org/licenses/MIT') }
it { expect(subject[:project_url]).to eq('https://gitlab.com/gitlab-org/gitlab') }
it { expect(subject[:icon_url]).to eq('https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png') }
end
context 'with invalid package file id' do context 'with invalid package file id' do
let(:package_file) { OpenStruct.new(id: 555) } let(:package_file) { OpenStruct.new(id: 555) }
......
# frozen_string_literal: true
require 'spec_helper'
describe Packages::Nuget::SyncMetadatumService do
let_it_be(:package, reload: true) { create(:nuget_package) }
let_it_be(:metadata) do
{
project_url: 'https://test.org/test',
license_url: 'https://test.org/MIT',
icon_url: 'https://test.org/icon.png'
}
end
let(:service) { described_class.new(package, metadata) }
let(:nuget_metadatum) { package.nuget_metadatum }
describe '#execute' do
subject { service.execute }
RSpec.shared_examples 'saving metadatum attributes' do
it 'saves nuget metadatum' do
subject
metadata.each do |attribute, expected_value|
expect(nuget_metadatum.send(attribute)).to eq(expected_value)
end
end
end
it 'creates a nuget metadatum' do
expect { subject }
.to change { package.nuget_metadatum.present? }.from(false).to(true)
end
it_behaves_like 'saving metadatum attributes'
context 'with exisiting nuget metadatum' do
let_it_be(:package) { create(:nuget_package, :with_metadatum) }
it 'does not create a nuget metadatum' do
expect { subject }.to change { ::Packages::Nuget::Metadatum.count }.by(0)
end
it_behaves_like 'saving metadatum attributes'
context 'with empty metadata' do
let_it_be(:metadata) { {} }
it 'destroys the nuget metadatum' do
expect { subject }
.to change { package.reload.nuget_metadatum.present? }.from(true).to(false)
end
end
end
end
end
...@@ -12,6 +12,12 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_ ...@@ -12,6 +12,12 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_
let(:package_version) { '1.0.0' } let(:package_version) { '1.0.0' }
let(:package_file_name) { 'dummyproject.dummypackage.1.0.0.nupkg' } let(:package_file_name) { 'dummyproject.dummypackage.1.0.0.nupkg' }
RSpec.shared_examples 'raising an' do |error_class|
it "raises an #{error_class}" do
expect { subject }.to raise_error(error_class)
end
end
describe '#execute' do describe '#execute' do
subject { service.execute } subject { service.execute }
...@@ -65,8 +71,10 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_ ...@@ -65,8 +71,10 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_
it 'updates package and package file' do it 'updates package and package file' do
expect { subject } expect { subject }
.to change { Packages::Dependency.count }.by(1) .to change { ::Packages::Package.count }.by(1)
.and change { Packages::Dependency.count }.by(1)
.and change { Packages::DependencyLink.count }.by(1) .and change { Packages::DependencyLink.count }.by(1)
.and change { ::Packages::Nuget::Metadatum.count }.by(0)
expect(package.reload.name).to eq(package_name) expect(package.reload.name).to eq(package_name)
expect(package.version).to eq(package_version) expect(package.version).to eq(package_version)
...@@ -92,6 +100,7 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_ ...@@ -92,6 +100,7 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_
.and change { Packages::Dependency.count }.by(0) .and change { Packages::Dependency.count }.by(0)
.and change { Packages::DependencyLink.count }.by(0) .and change { Packages::DependencyLink.count }.by(0)
.and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(0) .and change { Packages::Nuget::DependencyLinkMetadatum.count }.by(0)
.and change { ::Packages::Nuget::Metadatum.count }.by(0)
expect(package_file.reload.file_name).to eq(package_file_name) expect(package_file.reload.file_name).to eq(package_file_name)
expect(package_file.package).to eq(existing_package) expect(package_file.package).to eq(existing_package)
end end
...@@ -129,6 +138,29 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_ ...@@ -129,6 +138,29 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_
expect(existing_package.tags.map(&:name)).to contain_exactly(*expected_tags) expect(existing_package.tags.map(&:name)).to contain_exactly(*expected_tags)
end end
end end
it 'creates nuget metadatum' do
expect { subject }
.to change { ::Packages::Package.count }.by(1)
.and change { ::Packages::Nuget::Metadatum.count }.by(1)
metadatum = package_file.reload.package.nuget_metadatum
expect(metadatum.license_url).to eq('https://opensource.org/licenses/MIT')
expect(metadatum.project_url).to eq('https://gitlab.com/gitlab-org/gitlab')
expect(metadatum.icon_url).to eq('https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png')
end
context 'with too long url' do
let_it_be(:too_long_url) { "http://localhost/#{'bananas' * 50}" }
let(:metadata) { { package_name: package_name, package_version: package_version, license_url: too_long_url } }
before do
allow(service).to receive(:metadata).and_return(metadata)
end
it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
end
end end
context 'with nuspec file with dependencies' do context 'with nuspec file with dependencies' do
...@@ -163,9 +195,7 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_ ...@@ -163,9 +195,7 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_
allow_any_instance_of(Zip::File).to receive(:glob).and_return([]) allow_any_instance_of(Zip::File).to receive(:glob).and_return([])
end end
it 'raises an error' do it_behaves_like 'raising an', ::Packages::Nuget::MetadataExtractionService::ExtractionError
expect { subject }.to raise_error(::Packages::Nuget::MetadataExtractionService::ExtractionError)
end
end end
context 'with package file with a blank package name' do context 'with package file with a blank package name' do
...@@ -173,9 +203,7 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_ ...@@ -173,9 +203,7 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_
allow(service).to receive(:package_name).and_return('') allow(service).to receive(:package_name).and_return('')
end end
it 'raises an error' do it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
expect { subject }.to raise_error(::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError)
end
end end
context 'with package file with a blank package version' do context 'with package file with a blank package version' do
...@@ -183,9 +211,7 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_ ...@@ -183,9 +211,7 @@ describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_
allow(service).to receive(:package_version).and_return('') allow(service).to receive(:package_version).and_return('')
end end
it 'raises an error' do it_behaves_like 'raising an', ::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError
expect { subject }.to raise_error(::Packages::Nuget::UpdatePackageFromMetadataService::InvalidMetadataError)
end
end end
context 'with an invalid package version' do context 'with an invalid package version' do
......
...@@ -14513,6 +14513,9 @@ msgstr "" ...@@ -14513,6 +14513,9 @@ msgstr ""
msgid "Now you can access the merge request navigation tabs at the top, where they’re easier to find." msgid "Now you can access the merge request navigation tabs at the top, where they’re easier to find."
msgstr "" msgstr ""
msgid "Nuget metadatum must have at least license_url, project_url or icon_url set"
msgstr ""
msgid "Number of %{itemTitle}" msgid "Number of %{itemTitle}"
msgstr "" msgstr ""
......
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