Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-09-29 09:10:22 +00:00
parent 46ac076c84
commit d26aaa28dd
32 changed files with 459 additions and 148 deletions

View File

@ -10,7 +10,7 @@ module Mutations
include Mutations::WorkItems::UpdateArguments
include Mutations::WorkItems::Widgetable
authorize :update_work_item
authorize :read_work_item
field :work_item, Types::WorkItemType,
null: true,
@ -22,9 +22,11 @@ module Mutations
work_item = authorized_find!(id: id)
widget_params = extract_widget_params!(work_item.work_item_type, attributes)
interpret_quick_actions!(work_item, current_user, widget_params, attributes)
# Only checks permissions for base attributes because widgets define their own permissions independently
raise_resource_not_available_error! unless attributes.empty? || can_update?(work_item)
update_result = ::WorkItems::UpdateService.new(
container: work_item.project,
current_user: current_user,
@ -62,6 +64,10 @@ module Mutations
widget_params.merge!(parsed_params[:widgets])
attributes.merge!(parsed_params[:common])
end
def can_update?(work_item)
current_user.can?(:update_work_item, work_item)
end
end
end
end

View File

@ -11,6 +11,7 @@ module WorkItems
end
return if params.blank?
return unless has_permission?(:set_work_item_metadata)
service_params.merge!(params.slice(:add_label_ids, :remove_label_ids))
end

View File

@ -8,6 +8,7 @@ module WorkItems
return widget.work_item.assign_attributes({ start_date: nil, due_date: nil }) if new_type_excludes_widget?
return if params.blank?
return unless has_permission?(:set_work_item_metadata)
widget.work_item.assign_attributes(params.slice(:start_date, :due_date))
end

View File

@ -115,7 +115,7 @@ Upload a NuGet package file:
- For NuGet v2 feed:
```shell
```shell
curl --request PUT \
--form 'package=@path/to/mynugetpkg.1.3.0.17.nupkg' \
--user <username>:<personal_access_token> \
@ -425,6 +425,37 @@ Example response:
}
```
## Delete service
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38275) in GitLab 16.5.
Delete a NuGet package:
```plaintext
DELETE projects/:id/packages/nuget/:package_name/:package_version
```
| Attribute | Type | Required | Description |
| ----------------- | ------ | -------- | ----------- |
| `id` | string | yes | The ID or full path of the project. |
| `package_name` | string | yes | The name of the package. |
| `package_version` | string | yes | The version of the package. |
```shell
curl --request DELETE \
--user <username>:<personal_access_token> \
"https://gitlab.example.com/api/v4/projects/1/packages/nuget/MyNuGetPkg/1.3.0.17"
```
Possible request responses:
| Status | Description |
| ------ | ----------- |
| `204` | Package deleted |
| `401` | Unauthorized |
| `403` | Forbidden |
| `404` | Not found |
## V2 Feed Metadata Endpoints
> Introduced in GitLab 16.3.

View File

@ -40,7 +40,7 @@ To protect an environment:
- There are two roles to choose from:
- **Maintainers**: Allows access to all of the project's users with the Maintainer role.
- **Developers**: Allows access to all of the project's users with the Maintainer and Developer role.
- You can select groups that are already associated with the project only.
- You can only select groups that are already [invited](../../user/project/members/share_project_with_groups.md#share-a-project-with-a-group) to the project.
- Users must have at least the Developer role to appear in
the **Allowed to deploy** list.
1. In the **Approvers** list, select the role, users, or groups you
@ -49,7 +49,7 @@ To protect an environment:
- There are two roles to choose from:
- **Maintainers**: Allows access to all of the project's users with the Maintainer role.
- **Developers**: Allows access to all of the project's users with the Maintainer and Developer role.
- You can select groups that are already associated with the project only.
- You can only select groups that are already [invited](../../user/project/members/share_project_with_groups.md#share-a-project-with-a-group) to the project.
- Users must have at least the Developer role to appear in
the **Approvers** list.

View File

@ -72,10 +72,9 @@ To continue using registration tokens after GitLab 17.0:
Plans to implement a UI setting to re-enable registration tokens are proposed in [issue 411923](https://gitlab.com/gitlab-org/gitlab/-/issues/411923)
## Using runners registered with a runner registration token
## Impact on existing runners
Existing runners will not be affected by these changes and will continue to
work after the legacy registration method is removed in GitLab 18.0.
Existing runners will continue to work as usual even after 18.0. This change only affects registration of new runners.
## Changes to the `gitlab-runner register` command syntax
@ -140,10 +139,6 @@ for each job.
The specific runner can be identified by the unique system ID that is generated when the runner
process is started.
## Impact on existing runners
Existing runners will continue to work as usual. This change only affects registration of new runners.
## Creating runners programmatically
In GitLab 15.11 and later, you can use the [POST /user/runners REST API](../../api/users.md#create-a-runner)

View File

@ -2345,6 +2345,9 @@ In this example, a new pipeline causes a running pipeline to be:
like a build job. Deployment jobs usually shouldn't be cancelled, to prevent partial deployments.
- To completely cancel a running pipeline, all jobs must have `interruptible: true`,
or `interruptible: false` jobs must not have started.
- Running jobs are only cancelled if the newer pipeline has new changes.
For example, a running job is not be cancelled if you run a new pipeline for the same
commit by selecting **Run pipeline** in the UI.
### `needs`

View File

@ -1,6 +1,6 @@
---
stage: Create
group: Editor Extensions
group: Code Review
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

View File

@ -541,6 +541,36 @@ For example:
choco upgrade MyPackage -Source gitlab -Version 1.0.3
```
## Delete a package
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38275) in GitLab 16.5.
WARNING:
Deleting a package is a permanent action that cannot be undone.
Prerequisites:
- You must have the [Maintainer](../../../user/permissions.md#project-members-permissions) role or higher in the project.
- You must have both the package name and version.
To delete a package with the NuGet CLI:
```shell
nuget delete <package_id> <package_version> -Source <source_name> -ApiKey <gitlab_personal_access_token, deploy_token or job token>
```
In this command:
- `<package_id>` is the package ID.
- `<package_version>` is the package version.
- `<source_name>` is the source name.
For example:
```shell
nuget delete MyPackage 1.0.0 -Source gitlab -ApiKey <gitlab_personal_access_token, deploy_token or job token>
```
## Symbol packages
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/262081) in GitLab 14.1.
@ -561,6 +591,8 @@ for further updates.
## Supported CLI commands
> `nuget delete` and `dotnet nuget delete` commands [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38275) in GitLab 16.5.
The GitLab NuGet repository supports the following commands for the NuGet CLI (`nuget`) and the .NET
CLI (`dotnet`):
@ -568,6 +600,8 @@ CLI (`dotnet`):
- `dotnet nuget push`: Upload a package to the registry.
- `nuget install`: Install a package from the registry.
- `dotnet add`: Install a package from the registry.
- `nuget delete`: Delete a package from the registry.
- `dotnet nuget delete`: Delete a package from the registry.
## Example project

View File

@ -20,41 +20,6 @@ module API
NON_NEGATIVE_INTEGER_REGEX = %r{\A(0|[1-9]\d*)\z}
included do
helpers do
def find_packages(package_name)
packages = package_finder(package_name).execute
not_found!('Packages') unless packages.exists?
packages
end
def find_package(package_name, package_version)
package = package_finder(package_name, package_version).execute
.first
not_found!('Package') unless package
package
end
def package_finder(package_name, package_version = nil)
::Packages::Nuget::PackageFinder.new(
current_user,
project_or_group,
package_name: package_name,
package_version: package_version,
client_version: headers['X-Nuget-Client-Version']
)
end
def search_packages(_search_term, search_options)
::Packages::Nuget::SearchService
.new(current_user, project_or_group, params[:q], search_options)
.execute
end
end
# https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource
params do
requires :package_name, type: String, desc: 'The NuGet package name',

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
module API
module Helpers
module Packages
module Nuget
def find_packages(package_name)
packages = package_finder(package_name).execute
not_found!('Packages') unless packages.exists?
packages
end
def find_package(package_name, package_version)
package = package_finder(package_name, package_version).execute.first
not_found!('Package') unless package
package
end
def package_finder(package_name, package_version = nil)
::Packages::Nuget::PackageFinder.new(
current_user,
project_or_group,
package_name: package_name,
package_version: package_version,
client_version: headers['X-Nuget-Client-Version']
)
end
def search_packages(_search_term, search_options)
::Packages::Nuget::SearchService
.new(current_user, project_or_group, params[:q], search_options)
.execute
end
end
end
end
end

View File

@ -11,6 +11,7 @@ module API
class NugetGroupPackages < ::API::Base
helpers ::API::Helpers::PackagesHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
helpers ::API::Helpers::Packages::Nuget
include ::API::Helpers::Authentication
feature_category :package_registry

View File

@ -11,6 +11,7 @@ module API
class NugetProjectPackages < ::API::Base
helpers ::API::Helpers::PackagesHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
helpers ::API::Helpers::Packages::Nuget
include ::API::Helpers::Authentication
feature_category :package_registry
@ -233,7 +234,7 @@ module API
end
end
# To support an additional authentication option for publish endpoints,
# To support an additional authentication option for publish/delete endpoints,
# we redefine the `authenticate_with` method by combining the previous
# authentication option with the new one.
authenticate_with do |accept|
@ -311,6 +312,31 @@ module API
authorize_nuget_upload
end
desc 'The NuGet Package Delete endpoint' do
detail 'This feature was introduced in GitLab 16.5'
success code: 204
failure [
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not Found' }
]
tags %w[nuget_packages]
end
params do
requires :package_name, type: String, allow_blank: false, desc: 'The NuGet package name', regexp: Gitlab::Regex.nuget_package_name_regex, documentation: { example: 'mynugetpkg' }
requires :package_version, type: String, allow_blank: false, desc: 'The NuGet package version', regexp: Gitlab::Regex.nuget_version_regex, documentation: { example: '1.0.1' }
end
delete '*package_name/*package_version', format: false, urgency: :low do
authorize_destroy_package!(project_or_group)
package = find_package(params[:package_name], params[:package_version])
destroy_conditionally!(package) do |package|
::Packages::MarkPackageForDestructionService.new(container: package, current_user: current_user).execute
track_package_event('delete_package', :nuget, category: 'API::NugetPackages', project: package.project, namespace: package.project.namespace)
end
end
namespace '/v2' do
desc 'The NuGet V2 Feed Package Publish endpoint' do
detail 'This feature was introduced in GitLab 16.2'

View File

@ -68,7 +68,7 @@ module Backup
end
puts_time "Dumping #{definition.human_name} ... ".color(:blue)
definition.task.dump(File.join(Gitlab.config.backup.path, definition.destination_path), full_backup_id)
definition.task.dump(File.join(Gitlab.config.backup.path, definition.destination_path), backup_id)
puts_time "Dumping #{definition.human_name} ... ".color(:blue) + "done".color(:green)
rescue Backup::DatabaseBackupError, Backup::FileBackupError => e

View File

@ -12,15 +12,10 @@ image: python:latest
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
# Pip's cache doesn't store the python packages
# https://pip.pypa.io/en/stable/topics/caching/
#
# If you want to also cache the installed packages, you have to install
# them in a virtualenv and cache it as well.
cache:
paths:
- .cache/pip
- venv/
before_script:
- python --version ; pip --version # For debugging

View File

@ -71,6 +71,7 @@ module Gitlab
discussion_id: note.discussion_id,
noteable_id: merge_request_id,
project_id: project.id,
namespace_id: project.project_namespace_id,
author_id: author_id,
note: note_body,
commit_id: note.original_commit_id,

View File

@ -25,6 +25,7 @@ module Gitlab
noteable_type: note.noteable_type,
noteable_id: noteable_id,
project_id: project.id,
namespace_id: project.project_namespace_id,
author_id: author_id,
note: note_body(author_found),
discussion_id: note.discussion_id,
@ -36,8 +37,8 @@ module Gitlab
note = Note.new(attributes.merge(importing: true))
note.validate!
# We're using bulk_insert here so we can bypass any validations and
# callbacks. Running these would result in a lot of unnecessary SQL
# We're using bulk_insert here so we can bypass any callbacks.
# Running these would result in a lot of unnecessary SQL
# queries being executed when importing large projects.
# Note: if you're going to replace `legacy_bulk_insert` with something that trigger callback
# to generate HTML version - you also need to regenerate it in

View File

@ -67,6 +67,7 @@ module Gitlab
# always returns true by default for all workers unless the FF is specifically disabled, e.g. during an incident
Feature.enabled?(
:"#{RUN_FEATURE_FLAG_PREFIX}_#{worker_class.name}",
Feature.current_request,
type: :worker,
default_enabled_if_undefined: true
)
@ -94,6 +95,7 @@ module Gitlab
def drop_job?(worker_class)
Feature.enabled?(
:"#{DROP_FEATURE_FLAG_PREFIX}_#{worker_class.name}",
Feature.current_request,
type: :worker,
default_enabled_if_undefined: false
)

View File

@ -97,8 +97,9 @@ RUN set -eux; \
(cd gitlab && git init . && git add --all && git commit --quiet -m "Init repository") &> /dev/null; \
gdk config set gitaly.skip_setup true \
&& gdk config set workhorse.skip_setup true \
&& gdk config set gitlab_shell.skip_setup true; \
make redis/redis.conf all \
&& gdk config set gitlab_shell.skip_setup true \
&& cp .tool-versions ./gitlab/ \
&& make redis/redis.conf all \
&& gdk kill
ENTRYPOINT [ "/home/gdk/entrypoint" ]

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
require 'net/http'
module QA
RSpec.describe 'Create' do
describe 'Merge Requests', :reliable, product_group: :code_review do
let(:address) { Runtime::Address.new(:gitlab, path) }
context 'with a malformed URL' do
let(:path) { %(/-/merge_requests?sort=created_date&state=<th:t=\"%24{dfb}%23foreach) }
it 'returns 400', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/426509' do
# Ruby's URI module automatically encodes query parameters:
# https://github.com/ruby/uri/blob/f4999b61daa40f2c99fdc7159e2c85c036b22c67/lib/uri/generic.rb#L849
#
# This gets automatically used with HTTParty, Airborne, and other clients. We
# have to construct a malformed URL by building the request ourselves.
uri = URI.parse(address.address)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == 'https')
request = Net::HTTP::Get.new(path)
response = http.request(request)
expect(response.code.to_i).to eq(400)
end
end
end
end
end

View File

@ -31,7 +31,7 @@ RSpec.describe Types::BaseField, feature_category: :api do
end
it 'only tests the resolver authorization if it authorizes_object?' do
resolver = Class.new
resolver = Class.new(Resolvers::BaseResolver)
field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true,
resolver_class: resolver)
@ -40,7 +40,7 @@ RSpec.describe Types::BaseField, feature_category: :api do
end
it 'tests the resolver authorization, if provided' do
resolver = Class.new do
resolver = Class.new(Resolvers::BaseResolver) do
include Gitlab::Graphql::Authorize::AuthorizeResource
authorizes_object!
@ -55,7 +55,7 @@ RSpec.describe Types::BaseField, feature_category: :api do
end
it 'tests field authorization before resolver authorization, when field auth fails' do
resolver = Class.new do
resolver = Class.new(Resolvers::BaseResolver) do
include Gitlab::Graphql::Authorize::AuthorizeResource
authorizes_object!
@ -72,7 +72,7 @@ RSpec.describe Types::BaseField, feature_category: :api do
end
it 'tests field authorization before resolver authorization, when field auth succeeds' do
resolver = Class.new do
resolver = Class.new(Resolvers::BaseResolver) do
include Gitlab::Graphql::Authorize::AuthorizeResource
authorizes_object!
@ -91,7 +91,7 @@ RSpec.describe Types::BaseField, feature_category: :api do
context 'when considering complexity' do
let(:resolver) do
Class.new(described_class) do
Class.new(Resolvers::BaseResolver) do
def self.resolver_complexity(args, child_complexity:)
2 if args[:foo]
end

View File

@ -77,7 +77,7 @@ RSpec.describe Types::Ci::JobBaseField, feature_category: :runner_fleet do
end
context 'with field resolver' do
let(:resolver) { Class.new }
let(:resolver) { Class.new(Resolvers::BaseResolver) }
let(:args) { { resolver_class: resolver } }
it 'only tests the resolver authorization if it authorizes_object?' do
@ -86,7 +86,7 @@ RSpec.describe Types::Ci::JobBaseField, feature_category: :runner_fleet do
context 'when resolver authorizes object' do
let(:resolver) do
Class.new do
Class.new(Resolvers::BaseResolver) do
include Gitlab::Graphql::Authorize::AuthorizeResource
authorizes_object!

View File

@ -179,8 +179,8 @@ RSpec.describe Backup::Manager, feature_category: :backup_restore do
allow(Gitlab::BackupLogger).to receive(:info)
allow(Kernel).to receive(:system).and_return(true)
allow(task1).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'task1.tar.gz'), full_backup_id)
allow(task2).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'task2.tar.gz'), full_backup_id)
allow(task1).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'task1.tar.gz'), backup_id)
allow(task2).to receive(:dump).with(File.join(Gitlab.config.backup.path, 'task2.tar.gz'), backup_id)
end
it 'creates a backup tar' do

View File

@ -143,6 +143,7 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
expect(note.noteable_type).to eq('MergeRequest')
expect(note.noteable_id).to eq(merge_request.id)
expect(note.project_id).to eq(project.id)
expect(note.namespace_id).to eq(project.project_namespace_id)
expect(note.author_id).to eq(user.id)
expect(note.system).to eq(false)
expect(note.discussion_id).to eq(discussion_id)

View File

@ -50,6 +50,7 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteImporter do
noteable_type: 'Issue',
noteable_id: issue_row.id,
project_id: project.id,
namespace_id: project.project_namespace_id,
author_id: user.id,
note: 'This is my note',
discussion_id: match(/\A[0-9a-f]{40}\z/),
@ -81,6 +82,7 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteImporter do
noteable_type: 'Issue',
noteable_id: issue_row.id,
project_id: project.id,
namespace_id: project.project_namespace_id,
author_id: project.creator_id,
note: "*Created by: alice*\n\nThis is my note",
discussion_id: match(/\A[0-9a-f]{40}\z/),

View File

@ -23,77 +23,77 @@ RSpec.describe Gitlab::SidekiqMiddleware::SkipJobs, feature_category: :scalabili
describe '#call' do
context 'with worker not opted for database health check' do
let(:metric) { instance_double(Prometheus::Client::Counter, increment: true) }
shared_examples 'runs the job normally' do
it 'yields control' do
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.to yield_control
end
it 'does not increment any metric counter' do
expect(metric).not_to receive(:increment)
subject.call(TestWorker.new, job, queue) { nil }
end
it 'does not increment deferred_count' do
subject.call(TestWorker.new, job, queue) { nil }
expect(job).not_to include('deferred_count')
end
end
shared_examples 'drops the job' do
it 'does not yield control' do
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.not_to yield_control
end
it 'increments counter' do
expect(metric).to receive(:increment).with({ worker: "TestWorker", action: "dropped" })
subject.call(TestWorker.new, job, queue) { nil }
end
it 'does not increment deferred_count' do
subject.call(TestWorker.new, job, queue) { nil }
expect(job).not_to include('deferred_count')
end
it 'has dropped field in job equal to true' do
subject.call(TestWorker.new, job, queue) { nil }
expect(job).to include({ 'dropped' => true })
end
end
shared_examples 'defers the job' do
it 'does not yield control' do
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.not_to yield_control
end
it 'delays the job' do
expect(TestWorker).to receive(:perform_in).with(described_class::DELAY, *job['args'])
subject.call(TestWorker.new, job, queue) { nil }
end
it 'increments counter' do
expect(metric).to receive(:increment).with({ worker: "TestWorker", action: "deferred" })
subject.call(TestWorker.new, job, queue) { nil }
end
it 'has deferred related fields in job payload' do
subject.call(TestWorker.new, job, queue) { nil }
expect(job).to include({ 'deferred' => true, 'deferred_by' => :feature_flag, 'deferred_count' => 1 })
end
end
describe "with all combinations of drop and defer FFs" do
using RSpec::Parameterized::TableSyntax
let(:metric) { instance_double(Prometheus::Client::Counter, increment: true) }
shared_examples 'runs the job normally' do
it 'yields control' do
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.to yield_control
end
it 'does not increment any metric counter' do
expect(metric).not_to receive(:increment)
subject.call(TestWorker.new, job, queue) { nil }
end
it 'does not increment deferred_count' do
subject.call(TestWorker.new, job, queue) { nil }
expect(job).not_to include('deferred_count')
end
end
shared_examples 'drops the job' do
it 'does not yield control' do
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.not_to yield_control
end
it 'increments counter' do
expect(metric).to receive(:increment).with({ worker: "TestWorker", action: "dropped" })
subject.call(TestWorker.new, job, queue) { nil }
end
it 'does not increment deferred_count' do
subject.call(TestWorker.new, job, queue) { nil }
expect(job).not_to include('deferred_count')
end
it 'has dropped field in job equal to true' do
subject.call(TestWorker.new, job, queue) { nil }
expect(job).to include({ 'dropped' => true })
end
end
shared_examples 'defers the job' do
it 'does not yield control' do
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.not_to yield_control
end
it 'delays the job' do
expect(TestWorker).to receive(:perform_in).with(described_class::DELAY, *job['args'])
subject.call(TestWorker.new, job, queue) { nil }
end
it 'increments counter' do
expect(metric).to receive(:increment).with({ worker: "TestWorker", action: "deferred" })
subject.call(TestWorker.new, job, queue) { nil }
end
it 'has deferred related fields in job payload' do
subject.call(TestWorker.new, job, queue) { nil }
expect(job).to include({ 'deferred' => true, 'deferred_by' => :feature_flag, 'deferred_count' => 1 })
end
end
before do
stub_feature_flags("drop_sidekiq_jobs_#{TestWorker.name}": drop_ff)
stub_feature_flags("run_sidekiq_jobs_#{TestWorker.name}": run_ff)
@ -112,6 +112,45 @@ RSpec.describe Gitlab::SidekiqMiddleware::SkipJobs, feature_category: :scalabili
it_behaves_like params[:resulting_behavior]
end
end
describe 'using current_request actor', :request_store do
before do
allow(Gitlab::Metrics).to receive(:counter).and_call_original
allow(Gitlab::Metrics).to receive(:counter).with(described_class::COUNTER, anything).and_return(metric)
end
context 'with drop_sidekiq_jobs FF' do
before do
stub_feature_flags("drop_sidekiq_jobs_#{TestWorker.name}": Feature.current_request)
end
it_behaves_like 'drops the job'
context 'for different request' do
before do
stub_with_new_feature_current_request
end
it_behaves_like 'runs the job normally'
end
end
context 'with run_sidekiq_jobs FF' do
before do
stub_feature_flags("run_sidekiq_jobs_#{TestWorker.name}": Feature.current_request)
end
it_behaves_like 'runs the job normally'
context 'for different request' do
before do
stub_with_new_feature_current_request
end
it_behaves_like 'defers the job'
end
end
end
end
context 'with worker opted for database health check' do

View File

@ -1025,7 +1025,7 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
end
context 'when updating notifications subscription' do
let_it_be(:current_user) { reporter }
let_it_be(:current_user) { guest }
let(:input) { { 'notificationsWidget' => { 'subscribed' => desired_state } } }
let(:fields) do
@ -1059,7 +1059,7 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
update_work_item
subscription.reload
end.to change(subscription, :subscribed).to(desired_state)
.and(change { work_item.reload.subscribed?(reporter, project) }.to(desired_state))
.and(change { work_item.reload.subscribed?(guest, project) }.to(desired_state))
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['workItem']['widgets']).to include(
@ -1159,7 +1159,7 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
end
context 'when updating currentUserTodos' do
let_it_be(:current_user) { reporter }
let_it_be(:current_user) { guest }
let(:fields) do
<<~FIELDS
@ -1185,7 +1185,7 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
context 'when adding a new todo' do
let(:input) { { 'currentUserTodosWidget' => { 'action' => 'ADD' } } }
context 'when user has access to the work item' do
context 'when user can create todos' do
it 'adds a new todo for the user on the work item' do
expect { update_work_item }.to change { current_user.todos.count }.by(1)
@ -1203,6 +1203,17 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
}
)
end
context 'when a base attribute is present' do
before do
input.merge!('title' => 'new title')
end
it_behaves_like 'a mutation that returns top-level errors', errors: [
'The resource that you are attempting to access does not exist or you don\'t have permission to ' \
'perform this action'
]
end
end
context 'when user has no access' do

View File

@ -6,7 +6,6 @@ RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:project) { create(:project, :public) }
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let_it_be(:package_name) { 'Dummy.Package' }
@ -15,11 +14,9 @@ RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
let(:target_type) { 'projects' }
let(:snowplow_gitlab_standard_context) { snowplow_context }
def snowplow_context(user_role: :developer)
if user_role == :anonymous
{ project: target, namespace: target.namespace, property: 'i_package_nuget_user' }
else
{ project: target, namespace: target.namespace, property: 'i_package_nuget_user', user: user }
def snowplow_context(user_role: :developer, event_user: user)
{ project: target, namespace: target.namespace, property: 'i_package_nuget_user' }.tap do |context|
context[:user] = event_user unless user_role == :anonymous
end
end
@ -319,6 +316,97 @@ RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
end
end
describe 'DELETE /api/v4/projects/:id/packages/nuget/*package_name/*package_version' do
let_it_be(:package) { create(:nuget_package, project: project, name: package_name) }
let(:url) { "/projects/#{target.id}/packages/nuget/#{package_name}/#{package.version}" }
subject { delete api(url), headers: headers }
it { is_expected.to have_request_urgency(:low) }
context 'with valid target' do
where(:auth, :visibility, :user_role, :shared_examples_name, :expected_status) do
nil | :public | :anonymous | 'rejects nuget packages access' | :unauthorized
nil | :private | :anonymous | 'rejects nuget packages access' | :unauthorized
nil | :internal | :anonymous | 'rejects nuget packages access' | :unauthorized
:personal_access_token | :public | :guest | 'rejects nuget packages access' | :forbidden
:personal_access_token | :public | :developer | 'rejects nuget packages access' | :forbidden
:personal_access_token | :public | :maintainer | 'process nuget delete request' | :no_content
:personal_access_token | :private | :guest | 'rejects nuget packages access' | :forbidden
:personal_access_token | :private | :developer | 'rejects nuget packages access' | :forbidden
:personal_access_token | :private | :maintainer | 'process nuget delete request' | :no_content
:personal_access_token | :internal | :guest | 'rejects nuget packages access' | :forbidden
:personal_access_token | :internal | :developer | 'rejects nuget packages access' | :forbidden
:personal_access_token | :internal | :maintainer | 'process nuget delete request' | :no_content
:job_token | :public | :guest | 'rejects nuget packages access' | :forbidden
:job_token | :public | :developer | 'rejects nuget packages access' | :forbidden
:job_token | :public | :maintainer | 'process nuget delete request' | :no_content
:job_token | :private | :guest | 'rejects nuget packages access' | :forbidden
:job_token | :private | :developer | 'rejects nuget packages access' | :forbidden
:job_token | :private | :maintainer | 'process nuget delete request' | :no_content
:job_token | :internal | :guest | 'rejects nuget packages access' | :forbidden
:job_token | :internal | :developer | 'rejects nuget packages access' | :forbidden
:job_token | :internal | :maintainer | 'process nuget delete request' | :no_content
:deploy_token | :public | nil | 'process nuget delete request' | :no_content
:deploy_token | :private | nil | 'process nuget delete request' | :no_content
:deploy_token | :internal | nil | 'process nuget delete request' | :no_content
:api_key | :public | :guest | 'rejects nuget packages access' | :forbidden
:api_key | :public | :developer | 'rejects nuget packages access' | :forbidden
:api_key | :public | :maintainer | 'process nuget delete request' | :no_content
:api_key | :private | :guest | 'rejects nuget packages access' | :forbidden
:api_key | :private | :developer | 'rejects nuget packages access' | :forbidden
:api_key | :private | :maintainer | 'process nuget delete request' | :no_content
:api_key | :internal | :guest | 'rejects nuget packages access' | :forbidden
:api_key | :internal | :developer | 'rejects nuget packages access' | :forbidden
:api_key | :internal | :maintainer | 'process nuget delete request' | :no_content
end
with_them do
let(:snowplow_gitlab_standard_context) do
snowplow_context(user_role: user_role, event_user: auth == :deploy_token ? deploy_token : user)
end
let(:headers) do
case auth
when :personal_access_token
basic_auth_header(user.username, personal_access_token.token)
when :job_token
basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token)
when :deploy_token
basic_auth_header(deploy_token.username, deploy_token.token)
when :api_key
{ 'X-NuGet-ApiKey' => personal_access_token.token }
else
{}
end
end
before do
update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility.to_s.upcase, false))
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status]
end
end
it_behaves_like 'rejects nuget access with unknown target id'
it_behaves_like 'rejects nuget access with invalid target id'
['%20', '..%2F..', '../..'].each do |value|
context "with invalid package name #{value}" do
let(:package_name) { value }
it_behaves_like 'returning response status', :bad_request
end
end
end
describe 'PUT /api/v4/projects/:id/packages/nuget/v2/authorize' do
it_behaves_like 'nuget authorize upload endpoint' do
let(:url) { "/projects/#{target.id}/packages/nuget/v2/authorize" }

View File

@ -8,7 +8,7 @@ RSpec.describe WorkItems::Widgets::LabelsService::UpdateService, feature_categor
let_it_be(:label1) { create(:label, project: project) }
let_it_be(:label2) { create(:label, project: project) }
let_it_be(:label3) { create(:label, project: project) }
let_it_be(:current_user) { create(:user) }
let_it_be(:current_user) { create(:user).tap { |user| project.add_reporter(user) } }
let(:work_item) { create(:work_item, project: project, labels: [label1, label2]) }
let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Labels) } }
@ -26,6 +26,14 @@ RSpec.describe WorkItems::Widgets::LabelsService::UpdateService, feature_categor
}
)
end
context "and user doesn't have permissions to update labels" do
let_it_be(:current_user) { create(:user) }
it 'removes label params' do
expect(service.prepare_update_params(params: params)).to be_nil
end
end
end
context 'when widget does not exist in new type' do

View File

@ -3,8 +3,8 @@
require 'spec_helper'
RSpec.describe WorkItems::Widgets::StartAndDueDateService::UpdateService, feature_category: :portfolio_management do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user).tap { |user| project.add_reporter(user) } }
let_it_be_with_reload(:work_item) { create(:work_item, project: project) }
let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::StartAndDueDate) } }
@ -26,6 +26,14 @@ RSpec.describe WorkItems::Widgets::StartAndDueDateService::UpdateService, featur
change(work_item, :due_date).from(nil).to(due_date)
)
end
context "and user doesn't have permissions to update start and due date" do
let_it_be(:user) { create(:user) }
it 'removes start and due date params params' do
expect(update_params).to be_nil
end
end
end
context 'when date params are not present' do

View File

@ -6,5 +6,7 @@ RSpec.shared_context 'nuget api setup' do
include HttpBasicAuthHelpers
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:project) { create(:project, :public) }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
let_it_be_with_reload(:job) { create(:ci_build, user: user, status: :running, project: project) }
end

View File

@ -732,3 +732,19 @@ RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
end
end
end
RSpec.shared_examples 'process nuget delete request' do |user_type, status|
context "for user type #{user_type}" do
before do
target.send("add_#{user_type}", user) if user_type
end
it_behaves_like 'returning response status', status
it_behaves_like 'a package tracking event', 'API::NugetPackages', 'delete_package'
it 'marks package for deletion' do
expect { subject }.to change { package.reset.status }.from('default').to('pending_destruction')
end
end
end