Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
46ac076c84
commit
d26aaa28dd
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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" ]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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/),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue