Commit 384849e5 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '211926-support-nuget-dependencies-in-api-presenters' into 'master'

Support nuget dependencies in the API

See merge request gitlab-org/gitlab!33389
parents ff8f5459 65ffed50
......@@ -15,4 +15,5 @@ class Packages::DependencyLink < ApplicationRecord
scope :includes_dependency, -> { includes(:dependency) }
scope :for_package, ->(package) { where(package_id: package.id) }
scope :preload_dependency, -> { preload(:dependency) }
scope :preload_nuget_metadatum, -> { preload(:nuget_metadatum) }
end
......@@ -6,7 +6,8 @@ module Packages
include ::API::Helpers::RelatedResourcesHelpers
BLANK_STRING = ''
EMPTY_ARRAY = [].freeze
PACKAGE_DEPENDENCY_GROUP = 'PackageDependencyGroup'
PACKAGE_DEPENDENCY = 'PackageDependency'
private
......@@ -42,7 +43,7 @@ module Packages
{
json_url: json_url_for(package),
authors: BLANK_STRING,
dependencies: EMPTY_ARRAY,
dependency_groups: dependency_groups_for(package),
package_name: package.name,
package_version: package.version,
archive_url: archive_url_for(package),
......@@ -52,6 +53,46 @@ module Packages
}
end
def dependency_groups_for(package)
base_nuget_id = "#{json_url_for(package)}#dependencyGroup"
dependency_links_grouped_by_target_framework(package).map do |target_framework, dependency_links|
nuget_id = target_framework_nuget_id(base_nuget_id, target_framework)
{
id: nuget_id,
type: PACKAGE_DEPENDENCY_GROUP,
target_framework: target_framework,
dependencies: dependencies_for(nuget_id, dependency_links)
}.compact
end
end
def dependency_links_grouped_by_target_framework(package)
package
.dependency_links
.includes_dependency
.preload_nuget_metadatum
.group_by { |dependency_link| dependency_link.nuget_metadatum&.target_framework }
end
def dependencies_for(nuget_id, dependency_links)
return [] if dependency_links.empty?
dependency_links.map do |dependency_link|
dependency = dependency_link.dependency
{
id: "#{nuget_id}/#{dependency.name.downcase}",
type: PACKAGE_DEPENDENCY,
name: dependency.name,
range: dependency.version_pattern
}
end
end
def target_framework_nuget_id(base_nuget_id, target_framework)
target_framework.blank? ? base_nuget_id : "#{base_nuget_id}/#{target_framework.downcase}"
end
def metadatum_for(package)
metadatum = package.nuget_metadatum
return {} unless metadatum
......
---
title: Support nuget dependencies in the API
merge_request: 33389
author:
type: fixed
# frozen_string_literal: true
module EE
module API
module Entities
module Nuget
class Dependency < Grape::Entity
expose :id, as: :@id
expose :type, as: :@type
expose :name, as: :id
expose :range
end
end
end
end
end
# frozen_string_literal: true
module EE
module API
module Entities
module Nuget
class DependencyGroup < Grape::Entity
expose :id, as: :@id
expose :type, as: :@type
expose :target_framework, as: :targetFramework, expose_nil: false
expose :dependencies, using: EE::API::Entities::Nuget::Dependency
end
end
end
end
end
......@@ -7,7 +7,7 @@ module EE
class PackageMetadataCatalogEntry < Grape::Entity
expose :json_url, as: :@id
expose :authors
expose :dependencies, as: :dependencyGroups
expose :dependency_groups, as: :dependencyGroups, using: EE::API::Entities::Nuget::DependencyGroup
expose :package_name, as: :id
expose :package_version, as: :version
expose :tags
......
{
"type": "object",
"required": ["@id", "@type", "dependencies"],
"properties": {
"@id": { "type": "string" },
"@type": { "const": "PackageDependencyGroup" },
"targetFramework": { "type": "string" },
"dependencies": {
"type": "array",
"items": {
"type": "object",
"required": ["@id", "@type", "id", "range"],
"properties": {
"@id": { "type": "string" },
"@type": { "const": "PackageDependency" },
"id": { "type": "string" },
"range": { "type": "string" }
}
}
}
}
}
......@@ -10,7 +10,6 @@
"properties": {
"@id": { "type": "string" },
"authors": { "const": "" },
"dependencyGroups": { "const": [] },
"id": { "type": "string" },
"packageContent": { "type": "string" },
"summary": { "const": "" },
......@@ -18,7 +17,11 @@
"projectUrl": { "type": "string" },
"licenseUrl": { "type": "string" },
"iconUrl": { "type": "string" },
"version": { "type": "string" }
"version": { "type": "string" },
"dependencyGroups": {
"type": "array",
"items": { "$ref": "./dependency_group.json" }
}
}
}
}
......
......@@ -26,7 +26,6 @@
"properties": {
"@id": { "type": "string" },
"authors": { "const": "" },
"dependencyGroups": { "const": [] },
"id": { "type": "string" },
"packageContent": { "type": "string" },
"summary": { "const": "" },
......@@ -34,7 +33,11 @@
"projectUrl": { "type": "string" },
"licenseUrl": { "type": "string" },
"iconUrl": { "type": "string" },
"version": { "type": "string" }
"version": { "type": "string" },
"dependencyGroups": {
"type": "array",
"items": { "$ref": "./dependency_group.json" }
}
}
}
}
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe EE::API::Entities::Nuget::DependencyGroup do
let(:dependency_group) do
{
id: 'http://gitlab.com/Sandbox.App/1.0.0.json#dependencygroup',
type: 'PackageDependencyGroup',
target_framework: 'fwk test',
dependencies: [
{
id: 'http://gitlab.com/Sandbox.App/1.0.0.json#dependency',
type: 'PackageDependency',
name: 'Dependency',
range: '2.0.0'
}
]
}
end
let(:expected) do
{
'@id': 'http://gitlab.com/Sandbox.App/1.0.0.json#dependencygroup',
'@type': 'PackageDependencyGroup',
'targetFramework': 'fwk test',
'dependencies': [
{
'@id': 'http://gitlab.com/Sandbox.App/1.0.0.json#dependency',
'@type': 'PackageDependency',
'id': 'Dependency',
'range': '2.0.0'
}
]
}
end
let(:entity) { described_class.new(dependency_group) }
subject { entity.as_json }
it { is_expected.to eq(expected) }
context 'dependency group without target framework' do
let(:dependency_group_with_no_target_framework) { dependency_group.tap { |dg| dg[:target_framework] = nil } }
let(:expected_no_target_framework) { expected.except(:targetFramework) }
let(:entity) { described_class.new(dependency_group_with_no_target_framework) }
it { is_expected.to eq(expected_no_target_framework) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe EE::API::Entities::Nuget::Dependency do
let(:dependency) do
{
id: 'http://gitlab.com/Sandbox.App/1.0.0.json#dependency',
type: 'PackageDependency',
name: 'Dependency',
range: '2.0.0'
}
end
let(:expected) do
{
'@id': 'http://gitlab.com/Sandbox.App/1.0.0.json#dependency',
'@type': 'PackageDependency',
'id': 'Dependency',
'range': '2.0.0'
}
end
let(:entity) { described_class.new(dependency) }
subject { entity.as_json }
it { is_expected.to eq(expected) }
end
......@@ -7,7 +7,7 @@ RSpec.describe EE::API::Entities::Nuget::PackageMetadataCatalogEntry do
{
json_url: 'http://sandbox.com/json/package',
authors: 'Authors',
dependencies: [],
dependency_groups: [],
package_name: 'PackageTest',
package_version: '1.2.3',
tags: 'tag1 tag2 tag3',
......
......@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Packages::Nuget::PackageMetadataPresenter do
include_context 'with expected presenters dependency groups'
let_it_be(:package) { create(:nuget_package, :with_metadatum) }
let_it_be(:tag1) { create(:packages_tag, name: 'tag1', package: package) }
let_it_be(:tag2) { create(:packages_tag, name: 'tag2', package: package) }
......@@ -27,13 +29,17 @@ RSpec.describe Packages::Nuget::PackageMetadataPresenter do
describe '#catalog_entry' do
subject { presenter.catalog_entry }
before do
create_dependencies_for(package)
end
it 'returns an entry structure' do
entry = subject
expect(entry).to be_a Hash
%i[json_url archive_url].each { |field| expect(entry[field]).not_to be_blank }
%i[authors summary].each { |field| expect(entry[field]).to be_blank }
expect(entry[:dependencies]).to eq []
expect(entry[:dependency_groups]).to eq expected_dependency_groups(package.project_id, package.name, package.version)
expect(entry[:package_name]).to eq package.name
expect(entry[:package_version]).to eq package.version
expect(entry[:tags].split(::Packages::Tag::NUGET_TAGS_SEPARATOR)).to contain_exactly('tag1', 'tag2')
......
......@@ -3,7 +3,10 @@
require 'spec_helper'
RSpec.describe Packages::Nuget::PackagesMetadataPresenter do
let_it_be(:packages) { create_list(:nuget_package, 5, :with_metadatum, name: 'Dummy.Package') }
include_context 'with expected presenters dependency groups'
let_it_be(:project) { create(:project) }
let_it_be(:packages) { create_list(:nuget_package, 5, :with_metadatum, name: 'Dummy.Package', project: project) }
let_it_be(:presenter) { described_class.new(packages) }
describe '#count' do
......@@ -20,6 +23,8 @@ RSpec.describe Packages::Nuget::PackagesMetadataPresenter do
before do
packages.each do |pkg|
tag_names.each { |tag| create(:packages_tag, package: pkg, name: tag) }
create_dependencies_for(pkg)
end
end
......@@ -49,7 +54,7 @@ RSpec.describe Packages::Nuget::PackagesMetadataPresenter do
catalog_entry = pkg[:catalog_entry]
%i[json_url archive_url package_name package_version].each { |field| expect(catalog_entry[field]).not_to be_blank }
%i[authors summary].each { |field| expect(catalog_entry[field]).to be_blank }
expect(catalog_entry[:dependencies]).to eq []
expect(catalog_entry[:dependency_groups]).to eq(expected_dependency_groups(project.id, catalog_entry[:package_name], catalog_entry[:package_version]))
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|
......
......@@ -203,6 +203,8 @@ RSpec.describe API::NugetPackages do
end
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/index' do
include_context 'with expected presenters dependency groups'
let_it_be(:package_name) { 'Dummy.Package' }
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') } }
......@@ -210,6 +212,10 @@ RSpec.describe API::NugetPackages do
subject { get api(url) }
before do
packages.each { |pkg| create_dependencies_for(pkg) }
end
context 'with packages features enabled' do
before do
stub_licensed_features(packages: true)
......@@ -264,6 +270,8 @@ RSpec.describe API::NugetPackages do
end
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/*package_version' do
include_context 'with expected presenters dependency groups'
let_it_be(:package_name) { 'Dummy.Package' }
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') }
......@@ -271,6 +279,10 @@ RSpec.describe API::NugetPackages do
subject { get api(url) }
before do
create_dependencies_for(package)
end
context 'with packages features enabled' do
before do
stub_licensed_features(packages: true)
......
# frozen_string_literal: true
RSpec.shared_context 'with expected presenters dependency groups' do
def expected_dependency_groups(project_id, package_name, package_version)
[
{
id: "http://localhost/api/v4/projects/#{project_id}/packages/nuget/metadata/#{package_name}/#{package_version}.json#dependencyGroup/.netstandard2.0",
target_framework: '.NETStandard2.0',
type: 'PackageDependencyGroup',
dependencies: [
{
id: "http://localhost/api/v4/projects/#{project_id}/packages/nuget/metadata/#{package_name}/#{package_version}.json#dependencyGroup/.netstandard2.0/newtonsoft.json",
range: '12.0.3',
name: 'Newtonsoft.Json',
type: 'PackageDependency'
}
]
},
{
id: "http://localhost/api/v4/projects/#{project_id}/packages/nuget/metadata/#{package_name}/#{package_version}.json#dependencyGroup",
type: 'PackageDependencyGroup',
dependencies: [
{
id: "http://localhost/api/v4/projects/#{project_id}/packages/nuget/metadata/#{package_name}/#{package_version}.json#dependencyGroup/castle.core",
range: '4.4.1',
name: 'Castle.Core',
type: 'PackageDependency'
}
]
}
]
end
def create_dependencies_for(package)
dependency1 = Packages::Dependency.find_by(name: 'Newtonsoft.Json', version_pattern: '12.0.3') || create(:packages_dependency, name: 'Newtonsoft.Json', version_pattern: '12.0.3')
dependency2 = Packages::Dependency.find_by(name: 'Castle.Core', version_pattern: '4.4.1') || create(:packages_dependency, name: 'Castle.Core', version_pattern: '4.4.1')
create(:packages_dependency_link, :with_nuget_metadatum, package: package, dependency: dependency1)
create(:packages_dependency_link, package: package, dependency: dependency2)
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