Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/organizations/application_controller.rb1
-rw-r--r--app/finders/repositories/tree_finder.rb6
-rw-r--r--app/models/repository.rb4
-rw-r--r--app/models/tree.rb6
-rw-r--r--app/policies/organizations/organization_policy.rb7
-rw-r--r--app/presenters/packages/nuget/v2/metadata_index_presenter.rb48
-rw-r--r--config/feature_flags/development/handle_structured_gitaly_errors.yml8
-rw-r--r--doc/api/packages/nuget.md51
-rw-r--r--lib/api/concerns/packages/nuget/public_endpoints.rb16
-rw-r--r--lib/api/repositories.rb9
-rw-r--r--lib/gitlab/git/rugged_impl/tree.rb2
-rw-r--r--lib/gitlab/git/tree.rb17
-rw-r--r--package.json1
-rw-r--r--qa/qa/page/base.rb15
-rw-r--r--spec/finders/repositories/tree_finder_spec.rb4
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb39
-rw-r--r--spec/policies/organizations/organization_policy_spec.rb15
-rw-r--r--spec/presenters/packages/nuget/v2/metadata_index_presenter_spec.rb35
-rw-r--r--spec/requests/api/nuget_project_packages_spec.rb38
-rw-r--r--spec/requests/api/repositories_spec.rb46
-rw-r--r--spec/requests/organizations/organizations_controller_spec.rb44
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb28
-rw-r--r--yarn.lock5
23 files changed, 390 insertions, 55 deletions
diff --git a/app/controllers/organizations/application_controller.rb b/app/controllers/organizations/application_controller.rb
index bd2dcfacabe..568cfe6399d 100644
--- a/app/controllers/organizations/application_controller.rb
+++ b/app/controllers/organizations/application_controller.rb
@@ -2,6 +2,7 @@
module Organizations
class ApplicationController < ::ApplicationController
+ skip_before_action :authenticate_user!
before_action :organization
layout 'organization'
diff --git a/app/finders/repositories/tree_finder.rb b/app/finders/repositories/tree_finder.rb
index 231c1de1513..2a8971d4d86 100644
--- a/app/finders/repositories/tree_finder.rb
+++ b/app/finders/repositories/tree_finder.rb
@@ -13,7 +13,7 @@ module Repositories
def execute(gitaly_pagination: false)
raise CommitMissingError unless commit_exists?
- request_params = { recursive: recursive }
+ request_params = { recursive: recursive, rescue_not_found: rescue_not_found }
request_params[:pagination_params] = pagination_params if gitaly_pagination
repository.tree(commit.id, path, **request_params).sorted_entries
@@ -51,6 +51,10 @@ module Repositories
params[:recursive]
end
+ def rescue_not_found
+ params[:rescue_not_found]
+ end
+
def pagination_params
{
limit: params[:per_page] || Kaminari.config.default_per_page,
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 0bd26e7ad4c..b8a46f80bc7 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -697,7 +697,7 @@ class Repository
@head_tree ||= Tree.new(self, root_ref, nil, skip_flat_paths: skip_flat_paths)
end
- def tree(sha = :head, path = nil, recursive: false, skip_flat_paths: true, pagination_params: nil, ref_type: nil)
+ def tree(sha = :head, path = nil, recursive: false, skip_flat_paths: true, pagination_params: nil, ref_type: nil, rescue_not_found: true)
if sha == :head
return if empty? || root_ref.nil?
@@ -709,7 +709,7 @@ class Repository
end
end
- Tree.new(self, sha, path, recursive: recursive, skip_flat_paths: skip_flat_paths, pagination_params: pagination_params, ref_type: ref_type)
+ Tree.new(self, sha, path, recursive: recursive, skip_flat_paths: skip_flat_paths, pagination_params: pagination_params, ref_type: ref_type, rescue_not_found: rescue_not_found)
end
def blob_at_branch(branch_name, path)
diff --git a/app/models/tree.rb b/app/models/tree.rb
index 8622eb793c1..4d62334800d 100644
--- a/app/models/tree.rb
+++ b/app/models/tree.rb
@@ -7,7 +7,7 @@ class Tree
def initialize(
repository, sha, path = '/', recursive: false, skip_flat_paths: true, pagination_params: nil,
- ref_type: nil)
+ ref_type: nil, rescue_not_found: true)
path = '/' if path.blank?
@repository = repository
@@ -18,7 +18,9 @@ class Tree
ref = ExtractsRef.qualify_ref(@sha, ref_type)
- @entries, @cursor = Gitlab::Git::Tree.where(git_repo, ref, @path, recursive, skip_flat_paths, pagination_params)
+ @entries, @cursor = Gitlab::Git::Tree.where(git_repo, ref, @path, recursive, skip_flat_paths, rescue_not_found,
+ pagination_params)
+
@entries.each do |entry|
entry.ref_type = self.ref_type
end
diff --git a/app/policies/organizations/organization_policy.rb b/app/policies/organizations/organization_policy.rb
index 8cb4359b6fe..1c0d996c7d4 100644
--- a/app/policies/organizations/organization_policy.rb
+++ b/app/policies/organizations/organization_policy.rb
@@ -4,6 +4,13 @@ module Organizations
class OrganizationPolicy < BasePolicy
condition(:organization_user) { @subject.user?(@user) }
+ desc 'Organization is public'
+ condition(:public_organization, scope: :subject, score: 0) { true }
+
+ rule { public_organization }.policy do
+ enable :read_organization
+ end
+
rule { admin }.policy do
enable :admin_organization
enable :read_organization
diff --git a/app/presenters/packages/nuget/v2/metadata_index_presenter.rb b/app/presenters/packages/nuget/v2/metadata_index_presenter.rb
new file mode 100644
index 00000000000..0ce7c8956b3
--- /dev/null
+++ b/app/presenters/packages/nuget/v2/metadata_index_presenter.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Packages
+ module Nuget
+ module V2
+ class MetadataIndexPresenter
+ def xml
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
+ xml['edmx'].Edmx('xmlns:edmx' => 'http://schemas.microsoft.com/ado/2007/06/edmx', Version: '1.0') do
+ xml['edmx'].DataServices('xmlns:m' => 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata',
+ 'm:DataServiceVersion' => '2.0', 'm:MaxDataServiceVersion' => '2.0') do
+ xml.Schema(xmlns: 'http://schemas.microsoft.com/ado/2006/04/edm', Namespace: 'NuGetGallery.OData') do
+ xml.EntityType(Name: 'V2FeedPackage', 'm:HasStream' => true) do
+ xml.Key do
+ xml.PropertyRef(Name: 'Id')
+ xml.PropertyRef(Name: 'Version')
+ end
+ xml.Property(Name: 'Id', Type: 'Edm.String', Nullable: false)
+ xml.Property(Name: 'Version', Type: 'Edm.String', Nullable: false)
+ xml.Property(Name: 'Authors', Type: 'Edm.String')
+ xml.Property(Name: 'Dependencies', Type: 'Edm.String')
+ xml.Property(Name: 'Description', Type: 'Edm.String')
+ xml.Property(Name: 'DownloadCount', Type: 'Edm.Int64', Nullable: false)
+ xml.Property(Name: 'IconUrl', Type: 'Edm.String')
+ xml.Property(Name: 'Published', Type: 'Edm.DateTime', Nullable: false)
+ xml.Property(Name: 'ProjectUrl', Type: 'Edm.String')
+ xml.Property(Name: 'Tags', Type: 'Edm.String')
+ xml.Property(Name: 'Title', Type: 'Edm.String')
+ xml.Property(Name: 'LicenseUrl', Type: 'Edm.String')
+ end
+ end
+ xml.Schema(xmlns: 'http://schemas.microsoft.com/ado/2006/04/edm', Namespace: 'NuGetGallery') do
+ xml.EntityContainer(Name: 'V2FeedContext', 'm:IsDefaultEntityContainer' => true) do
+ xml.EntitySet(Name: 'Packages', EntityType: 'NuGetGallery.OData.V2FeedPackage')
+ xml.FunctionImport(Name: 'FindPackagesById',
+ ReturnType: 'Collection(NuGetGallery.OData.V2FeedPackage)', EntitySet: 'Packages') do
+ xml.Parameter(Name: 'id', Type: 'Edm.String', FixedLength: 'false', Unicode: 'false')
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/config/feature_flags/development/handle_structured_gitaly_errors.yml b/config/feature_flags/development/handle_structured_gitaly_errors.yml
new file mode 100644
index 00000000000..26a8082dec4
--- /dev/null
+++ b/config/feature_flags/development/handle_structured_gitaly_errors.yml
@@ -0,0 +1,8 @@
+---
+name: handle_structured_gitaly_errors
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128366
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/420865
+milestone: '16.3'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/doc/api/packages/nuget.md b/doc/api/packages/nuget.md
index aad4f0e9d45..a549d6af086 100644
--- a/doc/api/packages/nuget.md
+++ b/doc/api/packages/nuget.md
@@ -424,3 +424,54 @@ Example response:
]
}
```
+
+## V2 Feed Metadata Endpoint
+
+> Introduced in GitLab 16.3.
+
+Authentication is not required. Returns metadata for a V2 feed available endpoints:
+
+```plaintext
+GET <route-prefix>/v2/$metadata
+```
+
+```shell
+ curl "https://gitlab.example.com/api/v4/projects/1/packages/nuget/v2/$metadata"
+```
+
+Example response:
+
+```xml
+<edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" Version="1.0">
+ <edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="2.0" m:MaxDataServiceVersion="2.0">
+ <Schema xmlns="http://schemas.microsoft.com/ado/2006/04/edm" Namespace="NuGetGallery.OData">
+ <EntityType Name="V2FeedPackage" m:HasStream="true">
+ <Key>
+ <PropertyRef Name="Id"/>
+ <PropertyRef Name="Version"/>
+ </Key>
+ <Property Name="Id" Type="Edm.String" Nullable="false"/>
+ <Property Name="Version" Type="Edm.String" Nullable="false"/>
+ <Property Name="Authors" Type="Edm.String"/>
+ <Property Name="Dependencies" Type="Edm.String"/>
+ <Property Name="Description" Type="Edm.String"/>
+ <Property Name="DownloadCount" Type="Edm.Int64" Nullable="false"/>
+ <Property Name="IconUrl" Type="Edm.String"/>
+ <Property Name="Published" Type="Edm.DateTime" Nullable="false"/>
+ <Property Name="ProjectUrl" Type="Edm.String"/>
+ <Property Name="Tags" Type="Edm.String"/>
+ <Property Name="Title" Type="Edm.String"/>
+ <Property Name="LicenseUrl" Type="Edm.String"/>
+ </EntityType>
+ </Schema>
+ <Schema xmlns="http://schemas.microsoft.com/ado/2006/04/edm" Namespace="NuGetGallery">
+ <EntityContainer Name="V2FeedContext" m:IsDefaultEntityContainer="true">
+ <EntitySet Name="Packages" EntityType="NuGetGallery.OData.V2FeedPackage"/>
+ <FunctionImport Name="FindPackagesById" ReturnType="Collection(NuGetGallery.OData.V2FeedPackage)" EntitySet="Packages">
+ <Parameter Name="id" Type="Edm.String" FixedLength="false" Unicode="false"/>
+ </FunctionImport>
+ </EntityContainer>
+ </Schema>
+ </edmx:DataServices>
+</edmx:Edmx>
+```
diff --git a/lib/api/concerns/packages/nuget/public_endpoints.rb b/lib/api/concerns/packages/nuget/public_endpoints.rb
index d5be136c7a2..b0c9177f452 100644
--- a/lib/api/concerns/packages/nuget/public_endpoints.rb
+++ b/lib/api/concerns/packages/nuget/public_endpoints.rb
@@ -60,6 +60,22 @@ module API
.new(project_or_group_without_auth)
.xml
end
+
+ # https://www.nuget.org/api/v2/$metadata
+ desc 'The NuGet V2 Feed Package $metadata endpoint' do
+ detail 'This feature was introduced in GitLab 16.3'
+ success code: 200
+ tags %w[nuget_packages]
+ end
+
+ get '$metadata', format: :xml, urgency: :low do
+ env['api.format'] = :xml
+ content_type 'application/xml; charset=utf-8'
+ # needed to allow browser default inline styles in xml response
+ header 'Content-Security-Policy', "nonce-#{SecureRandom.base64(16)}"
+
+ present ::Packages::Nuget::V2::MetadataIndexPresenter.new.xml
+ end
end
end
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 295d1d5ab16..4131f41743f 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -95,6 +95,10 @@ module API
params
]
end
+
+ def rescue_not_found?
+ Feature.disabled?(:handle_structured_gitaly_errors)
+ end
end
desc 'Get a project repository tree' do
@@ -123,13 +127,16 @@ module API
end
end
get ':id/repository/tree', urgency: :low do
- tree_finder = ::Repositories::TreeFinder.new(user_project, declared_params(include_missing: false))
+ tree_finder = ::Repositories::TreeFinder.new(user_project, declared_params(include_missing: false).merge(rescue_not_found: rescue_not_found?))
not_found!("Tree") unless tree_finder.commit_exists?
tree = Gitlab::Pagination::GitalyKeysetPager.new(self, user_project).paginate(tree_finder)
present tree, with: Entities::TreeObject
+
+ rescue Gitlab::Git::Index::IndexError => e
+ not_found!(e.message)
end
desc 'Get raw blob contents from the repository'
diff --git a/lib/gitlab/git/rugged_impl/tree.rb b/lib/gitlab/git/rugged_impl/tree.rb
index c7a981c7dd4..bc3ff01e1e2 100644
--- a/lib/gitlab/git/rugged_impl/tree.rb
+++ b/lib/gitlab/git/rugged_impl/tree.rb
@@ -16,7 +16,7 @@ module Gitlab
TREE_SORT_ORDER = { tree: 0, blob: 1, commit: 2 }.freeze
override :tree_entries
- def tree_entries(repository, sha, path, recursive, skip_flat_paths, pagination_params = nil)
+ def tree_entries(repository, sha, path, recursive, skip_flat_paths, rescue_not_found, pagination_params = nil)
if use_rugged?(repository, :rugged_tree_entries)
entries = execute_rugged_call(
:tree_entries_with_flat_path_from_rugged, repository, sha, path, recursive, skip_flat_paths)
diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb
index 140dc791135..0895c0b8a22 100644
--- a/lib/gitlab/git/tree.rb
+++ b/lib/gitlab/git/tree.rb
@@ -15,22 +15,27 @@ module Gitlab
# Uses rugged for raw objects
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/320
- def where(repository, sha, path = nil, recursive = false, skip_flat_paths = true, pagination_params = nil)
+ def where(
+ repository, sha, path = nil, recursive = false, skip_flat_paths = true, rescue_not_found = true,
+ pagination_params = nil)
path = nil if path == '' || path == '/'
- tree_entries(repository, sha, path, recursive, skip_flat_paths, pagination_params)
+ tree_entries(repository, sha, path, recursive, skip_flat_paths, rescue_not_found, pagination_params)
end
- def tree_entries(repository, sha, path, recursive, skip_flat_paths, pagination_params = nil)
+ def tree_entries(repository, sha, path, recursive, skip_flat_paths, rescue_not_found, pagination_params = nil)
wrapped_gitaly_errors do
repository.gitaly_commit_client.tree_entries(
repository, sha, path, recursive, skip_flat_paths, pagination_params)
end
# Incorrect revision or path could lead to index error.
- # We silently handle such errors by returning an empty set of entries and cursor.
- rescue Gitlab::Git::Index::IndexError
- [[], nil]
+ # We silently handle such errors by returning an empty set of entries and cursor
+ # unless the parameter rescue_not_found is set to false.
+ rescue Gitlab::Git::Index::IndexError => e
+ return [[], nil] if rescue_not_found
+
+ raise e
end
private
diff --git a/package.json b/package.json
index 963b94db8b8..5ad960c7445 100644
--- a/package.json
+++ b/package.json
@@ -236,6 +236,7 @@
"@graphql-eslint/eslint-plugin": "3.20.1",
"@testing-library/dom": "^7.16.2",
"@types/jest": "^28.1.3",
+ "@types/lodash": "^4.14.197",
"@vue/compat": "^3.2.47",
"@vue/compiler-sfc": "^3.2.47",
"@vue/test-utils": "1.3.6",
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 456a6a564a3..1961e522b89 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -243,7 +243,20 @@ module QA
wait = kwargs.delete(:wait) || Capybara.default_max_wait_time
text = kwargs.delete(:text)
- find(element_selector_css(name, kwargs), text: text, wait: wait).click
+ begin
+ find(element_selector_css(name, kwargs), text: text, wait: wait).click
+ rescue Net::ReadTimeout => error
+ # In some situations due to perhaps a slow environment we can encounter errors
+ # where clicks are registered, but the calls to selenium-webdriver result in
+ # timeout errors. In these cases rescue from the error and attempt to continue in
+ # the test to avoid a flaky test failure. This should be safe as assertions in the
+ # tests will catch any case where the click wasn't actually registered.
+ QA::Runtime::Logger.warn "click_element -- #{error} -- #{error.backtrace.inspect}"
+ # There may be a 5xx error -- lets refresh the page like the warning page suggests
+ # and it if resolves itself we can avoid a flaky failure
+ refresh
+ end
+
page.validate_elements_present! if page
end
diff --git a/spec/finders/repositories/tree_finder_spec.rb b/spec/finders/repositories/tree_finder_spec.rb
index 0d70d5f92d3..42b4047c4e8 100644
--- a/spec/finders/repositories/tree_finder_spec.rb
+++ b/spec/finders/repositories/tree_finder_spec.rb
@@ -26,10 +26,10 @@ RSpec.describe Repositories::TreeFinder do
end
it "accepts a gitaly_pagination argument" do
- expect(repository).to receive(:tree).with(anything, anything, recursive: nil, pagination_params: { limit: 20, page_token: nil }).and_call_original
+ expect(repository).to receive(:tree).with(anything, anything, recursive: nil, rescue_not_found: nil, pagination_params: { limit: 20, page_token: nil }).and_call_original
expect(tree_finder.execute(gitaly_pagination: true)).to be_an(Array)
- expect(repository).to receive(:tree).with(anything, anything, recursive: nil).and_call_original
+ expect(repository).to receive(:tree).with(anything, anything, recursive: nil, rescue_not_found: nil).and_call_original
expect(tree_finder.execute(gitaly_pagination: false)).to be_an(Array)
end
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 4a20e0b1156..84ab8376fe1 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -9,13 +9,14 @@ RSpec.describe Gitlab::Git::Tree do
let(:repository) { project.repository.raw }
shared_examples 'repo' do
- subject(:tree) { Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, pagination_params) }
+ subject(:tree) { Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, rescue_not_found, pagination_params) }
let(:sha) { SeedRepo::Commit::ID }
let(:path) { nil }
let(:recursive) { false }
let(:pagination_params) { nil }
let(:skip_flat_paths) { false }
+ let(:rescue_not_found) { true }
let(:entries) { tree.first }
let(:cursor) { tree.second }
@@ -30,8 +31,14 @@ RSpec.describe Gitlab::Git::Tree do
context 'with an invalid ref' do
let(:sha) { 'foobar-does-not-exist' }
- it { expect(entries).to eq([]) }
- it { expect(cursor).to be_nil }
+ context 'when handle_structured_gitaly_errors feature is disabled' do
+ before do
+ stub_feature_flags(handle_structured_gitaly_errors: false)
+ end
+
+ it { expect(entries).to eq([]) }
+ it { expect(cursor).to be_nil }
+ end
end
context 'when path is provided' do
@@ -162,11 +169,23 @@ RSpec.describe Gitlab::Git::Tree do
end
context 'and invalid reference is used' do
- it 'returns no entries and nil cursor' do
+ before do
allow(repository.gitaly_commit_client).to receive(:tree_entries).and_raise(Gitlab::Git::Index::IndexError)
+ end
+
+ context 'when rescue_not_found is set to false' do
+ let(:rescue_not_found) { false }
- expect(entries.count).to eq(0)
- expect(cursor).to be_nil
+ it 'raises an IndexError error' do
+ expect { entries }.to raise_error(Gitlab::Git::Index::IndexError)
+ end
+ end
+
+ context 'when rescue_not_found is set to true' do
+ it 'returns no entries and nil cursor' do
+ expect(entries.count).to eq(0)
+ expect(cursor).to be_nil
+ end
end
end
end
@@ -196,7 +215,7 @@ RSpec.describe Gitlab::Git::Tree do
let(:entries_count) { entries.count }
it 'returns all entries without a cursor' do
- result, cursor = Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, { limit: entries_count, page_token: nil })
+ result, cursor = Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, rescue_not_found, { limit: entries_count, page_token: nil })
expect(cursor).to be_nil
expect(result.entries.count).to eq(entries_count)
@@ -225,7 +244,7 @@ RSpec.describe Gitlab::Git::Tree do
let(:entries_count) { entries.count }
it 'returns all entries' do
- result, cursor = Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, { limit: -1, page_token: nil })
+ result, cursor = Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, rescue_not_found, { limit: -1, page_token: nil })
expect(result.count).to eq(entries_count)
expect(cursor).to be_nil
@@ -236,7 +255,7 @@ RSpec.describe Gitlab::Git::Tree do
let(:token) { entries.second.id }
it 'returns all entries after token' do
- result, cursor = Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, { limit: -1, page_token: token })
+ result, cursor = Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, rescue_not_found, { limit: -1, page_token: token })
expect(result.count).to eq(entries.count - 2)
expect(cursor).to be_nil
@@ -268,7 +287,7 @@ RSpec.describe Gitlab::Git::Tree do
expected_entries = entries
loop do
- result, cursor = Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, { limit: 5, page_token: token })
+ result, cursor = Gitlab::Git::Tree.where(repository, sha, path, recursive, skip_flat_paths, rescue_not_found, { limit: 5, page_token: token })
collected_entries += result.entries
token = cursor&.next_cursor
diff --git a/spec/policies/organizations/organization_policy_spec.rb b/spec/policies/organizations/organization_policy_spec.rb
index 9914ce455d3..e51362227c9 100644
--- a/spec/policies/organizations/organization_policy_spec.rb
+++ b/spec/policies/organizations/organization_policy_spec.rb
@@ -7,6 +7,12 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
subject(:policy) { described_class.new(current_user, organization) }
+ context 'when the user is anonymous' do
+ let_it_be(:current_user) { nil }
+
+ it { is_expected.to be_allowed(:read_organization) }
+ end
+
context 'when the user is an admin' do
let_it_be(:current_user) { create(:user, :admin) }
@@ -17,7 +23,7 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:admin_organization) }
- it { is_expected.to be_disallowed(:read_organization) }
+ it { is_expected.to be_allowed(:read_organization) }
end
end
@@ -30,11 +36,4 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
it { is_expected.to be_allowed(:read_organization) }
end
-
- context 'when the user is not an organization user' do
- let_it_be(:current_user) { create :user }
-
- it { is_expected.to be_disallowed(:admin_organization) }
- it { is_expected.to be_disallowed(:read_organization) }
- end
end
diff --git a/spec/presenters/packages/nuget/v2/metadata_index_presenter_spec.rb b/spec/presenters/packages/nuget/v2/metadata_index_presenter_spec.rb
new file mode 100644
index 00000000000..598db641b75
--- /dev/null
+++ b/spec/presenters/packages/nuget/v2/metadata_index_presenter_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Nuget::V2::MetadataIndexPresenter, feature_category: :package_registry do
+ describe '#xml' do
+ let(:presenter) { described_class.new }
+
+ subject(:xml) { Nokogiri::XML(presenter.xml.to_xml) }
+
+ specify { expect(xml.root.name).to eq('Edmx') }
+
+ specify { expect(xml.at_xpath('//edmx:Edmx')).to be_present }
+
+ specify { expect(xml.at_xpath('//edmx:Edmx/edmx:DataServices')).to be_present }
+
+ specify do
+ expect(xml.css('*').map(&:name)).to include(
+ 'Schema', 'EntityType', 'Key', 'PropertyRef', 'EntityContainer', 'EntitySet', 'FunctionImport', 'Parameter'
+ )
+ end
+
+ specify do
+ expect(xml.css('*').select { |el| el.name == 'Property' }.map { |el| el.attribute_nodes.first.value })
+ .to match_array(
+ %w[Id Version Authors Dependencies Description DownloadCount IconUrl Published ProjectUrl Tags Title
+ LicenseUrl]
+ )
+ end
+
+ specify { expect(xml.css('*').detect { |el| el.name == 'EntityContainer' }.attr('Name')).to eq('V2FeedContext') }
+
+ specify { expect(xml.css('*').detect { |el| el.name == 'FunctionImport' }.attr('Name')).to eq('FindPackagesById') }
+ end
+end
diff --git a/spec/requests/api/nuget_project_packages_spec.rb b/spec/requests/api/nuget_project_packages_spec.rb
index 2d3781da42b..da74409cd77 100644
--- a/spec/requests/api/nuget_project_packages_spec.rb
+++ b/spec/requests/api/nuget_project_packages_spec.rb
@@ -50,6 +50,44 @@ RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
it_behaves_like 'accept get request on private project with access to package registry for everyone'
end
+ describe 'GET /api/v4/projects/:id/packages/nuget/v2/$metadata' do
+ let(:url) { "/projects/#{target.id}/packages/nuget/v2/$metadata" }
+
+ subject(:api_request) { get api(url) }
+
+ it { is_expected.to have_request_urgency(:low) }
+
+ context 'with valid target' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:visibility_level, :user_role, :member, :expected_status) do
+ 'PUBLIC' | :developer | true | :success
+ 'PUBLIC' | :guest | true | :success
+ 'PUBLIC' | :developer | false | :success
+ 'PUBLIC' | :guest | false | :success
+ 'PUBLIC' | :anonymous | false | :success
+ 'PRIVATE' | :developer | true | :success
+ 'PRIVATE' | :guest | true | :success
+ 'PRIVATE' | :developer | false | :success
+ 'PRIVATE' | :guest | false | :success
+ 'PRIVATE' | :anonymous | false | :success
+ 'INTERNAL' | :developer | true | :success
+ 'INTERNAL' | :guest | true | :success
+ 'INTERNAL' | :developer | false | :success
+ 'INTERNAL' | :guest | false | :success
+ 'INTERNAL' | :anonymous | false | :success
+ end
+
+ with_them do
+ before do
+ update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
+ end
+
+ it_behaves_like 'process nuget v2 $metadata service request', params[:user_role], params[:expected_status], params[:member]
+ end
+ end
+ end
+
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/index' do
let(:url) { "/projects/#{target.id}/packages/nuget/metadata/#{package_name}/index.json" }
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 8853eff0b3e..a94ed63bf47 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -37,6 +37,52 @@ RSpec.describe API::Repositories, feature_category: :source_code_management do
end
end
+ context 'when path does not exist' do
+ let(:path) { 'bogus' }
+
+ context 'when handle_structured_gitaly_errors feature is disabled' do
+ before do
+ stub_feature_flags(handle_structured_gitaly_errors: false)
+ end
+
+ it 'returns an empty array' do
+ get api("#{route}?path=#{path}", current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an(Array)
+ expect(json_response).to be_an_empty
+ end
+ end
+
+ context 'when handle_structured_gitaly_errors feature is enabled' do
+ before do
+ stub_feature_flags(handle_structured_gitaly_errors: true)
+ end
+
+ it_behaves_like '404 response' do
+ let(:request) { get api("#{route}?path=#{path}", current_user) }
+ let(:message) { '404 invalid revision or path Not Found' }
+ end
+ end
+ end
+
+ context 'when path is empty directory ' do
+ context 'when handle_structured_gitaly_errors feature is disabled' do
+ before do
+ stub_feature_flags(handle_structured_gitaly_errors: false)
+ end
+
+ it 'returns an empty array' do
+ get api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an(Array)
+ end
+ end
+ end
+
context 'when repository is disabled' do
include_context 'disabled repository'
diff --git a/spec/requests/organizations/organizations_controller_spec.rb b/spec/requests/organizations/organizations_controller_spec.rb
index 1dc3c3eaaa3..788d740504a 100644
--- a/spec/requests/organizations/organizations_controller_spec.rb
+++ b/spec/requests/organizations/organizations_controller_spec.rb
@@ -26,38 +26,40 @@ RSpec.describe Organizations::OrganizationsController, feature_category: :cell d
end
shared_examples 'basic organization controller action' do
- before do
- sign_in(user)
+ context 'when the user is not logged in' do
+ it_behaves_like 'successful response'
+ it_behaves_like 'action disabled by `ui_for_organizations` feature flag'
end
- context 'when the user does not have authorization' do
- let_it_be(:user) { create(:user) }
+ context 'when the user is logged in' do
+ before do
+ sign_in(user)
+ end
- it 'renders 404' do
- gitlab_request
+ context 'with no association to an organization' do
+ let_it_be(:user) { create(:user) }
- expect(response).to have_gitlab_http_status(:not_found)
+ it_behaves_like 'successful response'
+ it_behaves_like 'action disabled by `ui_for_organizations` feature flag'
end
- it_behaves_like 'action disabled by `ui_for_organizations` feature flag'
- end
+ context 'as as admin', :enable_admin_mode do
+ let_it_be(:user) { create(:admin) }
- context 'when the user is an admin', :enable_admin_mode do
- let_it_be(:user) { create(:admin) }
+ it_behaves_like 'successful response'
+ it_behaves_like 'action disabled by `ui_for_organizations` feature flag'
+ end
- it_behaves_like 'successful response'
- it_behaves_like 'action disabled by `ui_for_organizations` feature flag'
- end
+ context 'as an organization user' do
+ let_it_be(:user) { create :user }
- context 'when the user is an organization user' do
- let_it_be(:user) { create :user }
+ before do
+ create :organization_user, organization: organization, user: user
+ end
- before do
- create :organization_user, organization: organization, user: user
+ it_behaves_like 'successful response'
+ it_behaves_like 'action disabled by `ui_for_organizations` feature flag'
end
-
- it_behaves_like 'successful response'
- it_behaves_like 'action disabled by `ui_for_organizations` feature flag'
end
end
diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
index 5854958a06e..2e66bae26ba 100644
--- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
@@ -51,6 +51,34 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu
end
end
+RSpec.shared_examples 'process nuget v2 $metadata service request' do |user_type, status, add_member = true|
+ context "for user type #{user_type}" do
+ before do
+ target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
+ end
+
+ it_behaves_like 'returning response status', status
+
+ it 'returns a valid xml response' do
+ api_request
+
+ doc = Nokogiri::XML(body)
+
+ expect(response.media_type).to eq('application/xml')
+ expect(doc.at_xpath('//edmx:Edmx')).to be_present
+ expect(doc.at_xpath('//edmx:Edmx/edmx:DataServices')).to be_present
+ expect(doc.css('*').map(&:name)).to include(
+ 'Schema', 'EntityType', 'Key', 'PropertyRef', 'EntityContainer', 'EntitySet', 'FunctionImport', 'Parameter'
+ )
+ expect(doc.css('*').select { |el| el.name == 'Property' }.map { |el| el.attribute_nodes.first.value })
+ .to match_array(%w[Id Version Authors Dependencies Description DownloadCount IconUrl Published ProjectUrl
+ Tags Title LicenseUrl]
+ )
+ expect(doc.css('*').detect { |el| el.name == 'FunctionImport' }.attr('Name')).to eq('FindPackagesById')
+ 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
diff --git a/yarn.lock b/yarn.lock
index 4a254c2fe77..e6c40b084f7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2324,6 +2324,11 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
+"@types/lodash@^4.14.197":
+ version "4.14.197"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.197.tgz#e95c5ddcc814ec3e84c891910a01e0c8a378c54b"
+ integrity sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==
+
"@types/mdast@^3.0.0":
version "3.0.10"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"