Skip to content
代码片段 群组 项目
未验证 提交 dd2c7c3a 编辑于 作者: Gerardo Navarro's avatar Gerardo Navarro 提交者: GitLab
浏览文件

Protected packages: Add minimum_access_level_for_delete to GRAPHQL API

The attribute `minimum_access_level_for_delete` that was introduced
in the MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/179739 .

This commit includes the attribute `minimum_access_level_for_delete`
in the GRAPHQL API and also updates the documentation.

Changelog: added
上级 2fafef95
No related branches found
No related tags found
2 合并请求!3031Merge per-main-jh to main-jh by luzhiyuan,!3030Merge per-main-jh to main-jh
显示
258 个添加35 个删除
...@@ -27,9 +27,15 @@ class Create < ::Mutations::BaseMutation ...@@ -27,9 +27,15 @@ class Create < ::Mutations::BaseMutation
required: true, required: true,
description: copy_field_description(Types::Packages::Protection::RuleType, :package_type) description: copy_field_description(Types::Packages::Protection::RuleType, :package_type)
argument :minimum_access_level_for_delete,
Types::Packages::Protection::RuleAccessLevelForDeleteEnum,
required: false,
experiment: { milestone: '17.10' },
description: copy_field_description(Types::Packages::Protection::RuleType, :minimum_access_level_for_delete)
argument :minimum_access_level_for_push, argument :minimum_access_level_for_push,
Types::Packages::Protection::RuleAccessLevelEnum, Types::Packages::Protection::RuleAccessLevelEnum,
required: true, required: false,
description: copy_field_description(Types::Packages::Protection::RuleType, :minimum_access_level_for_push) description: copy_field_description(Types::Packages::Protection::RuleType, :minimum_access_level_for_push)
field :package_protection_rule, field :package_protection_rule,
...@@ -40,8 +46,13 @@ class Create < ::Mutations::BaseMutation ...@@ -40,8 +46,13 @@ class Create < ::Mutations::BaseMutation
def resolve(project_path:, **kwargs) def resolve(project_path:, **kwargs)
project = authorized_find!(project_path) project = authorized_find!(project_path)
response = ::Packages::Protection::CreateRuleService.new(project: project, current_user: current_user, kwargs.except!(:minimum_access_level_for_delete) if Feature.disabled?(:packages_protected_packages_delete,
params: kwargs).execute project)
response =
::Packages::Protection::CreateRuleService
.new(project: project, current_user: current_user, params: kwargs)
.execute
{ package_protection_rule: response.payload[:package_protection_rule], errors: response.errors } { package_protection_rule: response.payload[:package_protection_rule], errors: response.errors }
end end
......
...@@ -28,10 +28,15 @@ class Update < ::Mutations::BaseMutation ...@@ -28,10 +28,15 @@ class Update < ::Mutations::BaseMutation
validates: { allow_blank: false }, validates: { allow_blank: false },
description: copy_field_description(Types::Packages::Protection::RuleType, :package_type) description: copy_field_description(Types::Packages::Protection::RuleType, :package_type)
argument :minimum_access_level_for_delete,
Types::Packages::Protection::RuleAccessLevelForDeleteEnum,
required: false,
experiment: { milestone: '17.10' },
description: copy_field_description(Types::Packages::Protection::RuleType, :minimum_access_level_for_delete)
argument :minimum_access_level_for_push, argument :minimum_access_level_for_push,
Types::Packages::Protection::RuleAccessLevelEnum, Types::Packages::Protection::RuleAccessLevelEnum,
required: false, required: false,
validates: { allow_blank: false },
description: copy_field_description(Types::Packages::Protection::RuleType, :minimum_access_level_for_push) description: copy_field_description(Types::Packages::Protection::RuleType, :minimum_access_level_for_push)
field :package_protection_rule, field :package_protection_rule,
...@@ -42,6 +47,9 @@ class Update < ::Mutations::BaseMutation ...@@ -42,6 +47,9 @@ class Update < ::Mutations::BaseMutation
def resolve(id:, **kwargs) def resolve(id:, **kwargs)
package_protection_rule = authorized_find!(id: id) package_protection_rule = authorized_find!(id: id)
kwargs.except!(:minimum_access_level_for_delete) if Feature.disabled?(:packages_protected_packages_delete,
package_protection_rule.project)
response = ::Packages::Protection::UpdateRuleService.new(package_protection_rule, response = ::Packages::Protection::UpdateRuleService.new(package_protection_rule,
current_user: current_user, params: kwargs).execute current_user: current_user, params: kwargs).execute
......
# frozen_string_literal: true
module Types
module Packages
module Protection
class RuleAccessLevelForDeleteEnum < BaseEnum
graphql_name 'PackagesProtectionRuleAccessLevelForDelete'
description 'Access level for the deletion of a package protection rule resource.'
::Packages::Protection::Rule.minimum_access_level_for_deletes.each_key do |access_level_key|
value access_level_key.upcase,
value: access_level_key.to_s,
description: "#{access_level_key.capitalize} access. " \
'Available only when feature flag `packages_protected_packages_delete` is enabled.'
end
end
end
end
end
...@@ -27,13 +27,29 @@ class RuleType < ::Types::BaseObject ...@@ -27,13 +27,29 @@ class RuleType < ::Types::BaseObject
null: false, null: false,
description: 'Package type protected by the protection rule. For example, `NPM`, `PYPI`.' description: 'Package type protected by the protection rule. For example, `NPM`, `PYPI`.'
field :minimum_access_level_for_delete,
Types::Packages::Protection::RuleAccessLevelForDeleteEnum,
null: true,
experiment: { milestone: '17.10' },
description:
'Minimum GitLab access required to delete packages from the package registry. ' \
'Valid values include `OWNER` or `ADMIN`. ' \
'If the value is `nil`, the default minimum access level is `MAINTAINER`. ' \
'Available only when feature flag `packages_protected_packages_delete` is enabled.'
field :minimum_access_level_for_push, field :minimum_access_level_for_push,
Types::Packages::Protection::RuleAccessLevelEnum, Types::Packages::Protection::RuleAccessLevelEnum,
null: false, null: true,
description: description:
'Minimum GitLab access required to push packages to the package registry. ' \ 'Minimum GitLab access required to push packages to the package registry. ' \
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \ 'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \
'If the value is `nil`, the default minimum access level is `DEVELOPER`.' 'If the value is `nil`, the default minimum access level is `DEVELOPER`.'
def minimum_access_level_for_delete
return unless Feature.enabled?(:packages_protected_packages_delete, object&.project)
object.minimum_access_level_for_delete
end
end end
end end
end end
......
...@@ -6,6 +6,7 @@ class CreateRuleService < BaseProjectService ...@@ -6,6 +6,7 @@ class CreateRuleService < BaseProjectService
ALLOWED_ATTRIBUTES = %i[ ALLOWED_ATTRIBUTES = %i[
package_name_pattern package_name_pattern
package_type package_type
minimum_access_level_for_delete
minimum_access_level_for_push minimum_access_level_for_push
].freeze ].freeze
......
...@@ -7,8 +7,7 @@ class DeleteRuleService ...@@ -7,8 +7,7 @@ class DeleteRuleService
def initialize(package_protection_rule, current_user:) def initialize(package_protection_rule, current_user:)
if package_protection_rule.blank? || current_user.blank? if package_protection_rule.blank? || current_user.blank?
raise ArgumentError, raise ArgumentError, 'package_protection_rule and current_user must be set'
'package_protection_rule and current_user must be set'
end end
@package_protection_rule = package_protection_rule @package_protection_rule = package_protection_rule
......
...@@ -8,6 +8,7 @@ class UpdateRuleService ...@@ -8,6 +8,7 @@ class UpdateRuleService
ALLOWED_ATTRIBUTES = %i[ ALLOWED_ATTRIBUTES = %i[
package_name_pattern package_name_pattern
package_type package_type
minimum_access_level_for_delete
minimum_access_level_for_push minimum_access_level_for_push
].freeze ].freeze
......
...@@ -4367,7 +4367,8 @@ Input type: `CreatePackagesProtectionRuleInput` ...@@ -4367,7 +4367,8 @@ Input type: `CreatePackagesProtectionRuleInput`
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="mutationcreatepackagesprotectionruleclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationcreatepackagesprotectionruleclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcreatepackagesprotectionruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`PackagesProtectionRuleAccessLevel!`](#packagesprotectionruleaccesslevel) | Minimum GitLab access required to push packages to the package registry. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. | | <a id="mutationcreatepackagesprotectionruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`PackagesProtectionRuleAccessLevelForDelete`](#packagesprotectionruleaccesslevelfordelete) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 17.10. |
| <a id="mutationcreatepackagesprotectionruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`PackagesProtectionRuleAccessLevel`](#packagesprotectionruleaccesslevel) | Minimum GitLab access required to push packages to the package registry. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. |
| <a id="mutationcreatepackagesprotectionrulepackagenamepattern"></a>`packageNamePattern` | [`String!`](#string) | Package name protected by the protection rule. For example, `@my-scope/my-package-*`. Wildcard character `*` allowed. | | <a id="mutationcreatepackagesprotectionrulepackagenamepattern"></a>`packageNamePattern` | [`String!`](#string) | Package name protected by the protection rule. For example, `@my-scope/my-package-*`. Wildcard character `*` allowed. |
| <a id="mutationcreatepackagesprotectionrulepackagetype"></a>`packageType` | [`PackagesProtectionRulePackageType!`](#packagesprotectionrulepackagetype) | Package type protected by the protection rule. For example, `NPM`, `PYPI`. | | <a id="mutationcreatepackagesprotectionrulepackagetype"></a>`packageType` | [`PackagesProtectionRulePackageType!`](#packagesprotectionrulepackagetype) | Package type protected by the protection rule. For example, `NPM`, `PYPI`. |
| <a id="mutationcreatepackagesprotectionruleprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project where a protection rule is located. | | <a id="mutationcreatepackagesprotectionruleprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project where a protection rule is located. |
...@@ -11514,6 +11515,7 @@ Input type: `UpdatePackagesProtectionRuleInput` ...@@ -11514,6 +11515,7 @@ Input type: `UpdatePackagesProtectionRuleInput`
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="mutationupdatepackagesprotectionruleclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationupdatepackagesprotectionruleclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationupdatepackagesprotectionruleid"></a>`id` | [`PackagesProtectionRuleID!`](#packagesprotectionruleid) | Global ID of the package protection rule to be updated. | | <a id="mutationupdatepackagesprotectionruleid"></a>`id` | [`PackagesProtectionRuleID!`](#packagesprotectionruleid) | Global ID of the package protection rule to be updated. |
| <a id="mutationupdatepackagesprotectionruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`PackagesProtectionRuleAccessLevelForDelete`](#packagesprotectionruleaccesslevelfordelete) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 17.10. |
| <a id="mutationupdatepackagesprotectionruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`PackagesProtectionRuleAccessLevel`](#packagesprotectionruleaccesslevel) | Minimum GitLab access required to push packages to the package registry. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. | | <a id="mutationupdatepackagesprotectionruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`PackagesProtectionRuleAccessLevel`](#packagesprotectionruleaccesslevel) | Minimum GitLab access required to push packages to the package registry. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. |
| <a id="mutationupdatepackagesprotectionrulepackagenamepattern"></a>`packageNamePattern` | [`String`](#string) | Package name protected by the protection rule. For example, `@my-scope/my-package-*`. Wildcard character `*` allowed. | | <a id="mutationupdatepackagesprotectionrulepackagenamepattern"></a>`packageNamePattern` | [`String`](#string) | Package name protected by the protection rule. For example, `@my-scope/my-package-*`. Wildcard character `*` allowed. |
| <a id="mutationupdatepackagesprotectionrulepackagetype"></a>`packageType` | [`PackagesProtectionRulePackageType`](#packagesprotectionrulepackagetype) | Package type protected by the protection rule. For example, `NPM`, `PYPI`. | | <a id="mutationupdatepackagesprotectionrulepackagetype"></a>`packageType` | [`PackagesProtectionRulePackageType`](#packagesprotectionrulepackagetype) | Package type protected by the protection rule. For example, `NPM`, `PYPI`. |
...@@ -33076,7 +33078,8 @@ A packages protection rule designed to protect packages from being pushed by use ...@@ -33076,7 +33078,8 @@ A packages protection rule designed to protect packages from being pushed by use
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="packagesprotectionruleid"></a>`id` | [`PackagesProtectionRuleID!`](#packagesprotectionruleid) | Global ID of the package protection rule. | | <a id="packagesprotectionruleid"></a>`id` | [`PackagesProtectionRuleID!`](#packagesprotectionruleid) | Global ID of the package protection rule. |
| <a id="packagesprotectionruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`PackagesProtectionRuleAccessLevel!`](#packagesprotectionruleaccesslevel) | Minimum GitLab access required to push packages to the package registry. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. | | <a id="packagesprotectionruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`PackagesProtectionRuleAccessLevelForDelete`](#packagesprotectionruleaccesslevelfordelete) | **Introduced** in GitLab 17.10. **Status**: Experiment. Minimum GitLab access required to delete packages from the package registry. Valid values include `OWNER` or `ADMIN`. If the value is `nil`, the default minimum access level is `MAINTAINER`. Available only when feature flag `packages_protected_packages_delete` is enabled. |
| <a id="packagesprotectionruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`PackagesProtectionRuleAccessLevel`](#packagesprotectionruleaccesslevel) | Minimum GitLab access required to push packages to the package registry. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. |
| <a id="packagesprotectionrulepackagenamepattern"></a>`packageNamePattern` | [`String!`](#string) | Package name protected by the protection rule. For example, `@my-scope/my-package-*`. Wildcard character `*` allowed. | | <a id="packagesprotectionrulepackagenamepattern"></a>`packageNamePattern` | [`String!`](#string) | Package name protected by the protection rule. For example, `@my-scope/my-package-*`. Wildcard character `*` allowed. |
| <a id="packagesprotectionrulepackagetype"></a>`packageType` | [`PackagesProtectionRulePackageType!`](#packagesprotectionrulepackagetype) | Package type protected by the protection rule. For example, `NPM`, `PYPI`. | | <a id="packagesprotectionrulepackagetype"></a>`packageType` | [`PackagesProtectionRulePackageType!`](#packagesprotectionrulepackagetype) | Package type protected by the protection rule. For example, `NPM`, `PYPI`. |
   
...@@ -43357,6 +43360,15 @@ Access level of a package protection rule resource. ...@@ -43357,6 +43360,15 @@ Access level of a package protection rule resource.
| <a id="packagesprotectionruleaccesslevelmaintainer"></a>`MAINTAINER` | Maintainer access. | | <a id="packagesprotectionruleaccesslevelmaintainer"></a>`MAINTAINER` | Maintainer access. |
| <a id="packagesprotectionruleaccesslevelowner"></a>`OWNER` | Owner access. | | <a id="packagesprotectionruleaccesslevelowner"></a>`OWNER` | Owner access. |
   
### `PackagesProtectionRuleAccessLevelForDelete`
Access level for the deletion of a package protection rule resource.
| Value | Description |
| ----- | ----------- |
| <a id="packagesprotectionruleaccesslevelfordeleteadmin"></a>`ADMIN` | Admin access. Available only when feature flag `packages_protected_packages_delete` is enabled. |
| <a id="packagesprotectionruleaccesslevelfordeleteowner"></a>`OWNER` | Owner access. Available only when feature flag `packages_protected_packages_delete` is enabled. |
### `PackagesProtectionRulePackageType` ### `PackagesProtectionRulePackageType`
   
Package type of a package protection rule resource. Package type of a package protection rule resource.
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['PackagesProtectionRuleAccessLevelForDelete'], feature_category: :package_registry do
it 'exposes all options' do
expect(described_class.values.keys).to match_array(%w[OWNER ADMIN])
end
end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe GitlabSchema.types['PackagesProtectionRule'], feature_category: :package_registry do RSpec.describe GitlabSchema.types['PackagesProtectionRule'], feature_category: :package_registry do
include GraphqlHelpers
specify { expect(described_class.graphql_name).to eq('PackagesProtectionRule') } specify { expect(described_class.graphql_name).to eq('PackagesProtectionRule') }
specify { expect(described_class.description).to be_present } specify { expect(described_class.description).to be_present }
...@@ -27,9 +29,35 @@ ...@@ -27,9 +29,35 @@
it { is_expected.to have_non_null_graphql_type(Types::Packages::Protection::RulePackageTypeEnum) } it { is_expected.to have_non_null_graphql_type(Types::Packages::Protection::RulePackageTypeEnum) }
end end
describe 'minimum_access_level_for_delete' do
subject { described_class.fields['minimumAccessLevelForDelete'] }
it { is_expected.to have_nullable_graphql_type(Types::Packages::Protection::RuleAccessLevelForDeleteEnum) }
describe 'resolve field' do
let_it_be(:package_protection_rule) { create(:package_protection_rule) }
let(:user) { package_protection_rule.project.owner }
subject do
resolve_field(:minimum_access_level_for_delete, package_protection_rule, current_user: user,
object_type: described_class)
end
it { is_expected.to eq 'owner' }
context 'when the feature flag `packages_protected_packages_delete` is disabled' do
before do
stub_feature_flags(packages_protected_packages_delete: false)
end
it { is_expected.to be_nil }
end
end
end
describe 'minimum_access_level_for_push' do describe 'minimum_access_level_for_push' do
subject { described_class.fields['minimumAccessLevelForPush'] } subject { described_class.fields['minimumAccessLevelForPush'] }
it { is_expected.to have_non_null_graphql_type(Types::Packages::Protection::RuleAccessLevelEnum) } it { is_expected.to have_nullable_graphql_type(Types::Packages::Protection::RuleAccessLevelEnum) }
end end
end end
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
project_path: project.full_path, project_path: project.full_path,
package_name_pattern: package_protection_rule_attributes.package_name_pattern, package_name_pattern: package_protection_rule_attributes.package_name_pattern,
package_type: 'NPM', package_type: 'NPM',
minimum_access_level_for_delete: 'OWNER',
minimum_access_level_for_push: 'MAINTAINER' minimum_access_level_for_push: 'MAINTAINER'
} }
end end
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
id id
packageNamePattern packageNamePattern
packageType packageType
minimumAccessLevelForDelete
minimumAccessLevelForPush minimumAccessLevelForPush
} }
errors errors
...@@ -54,9 +56,10 @@ ...@@ -54,9 +56,10 @@
expect(mutation_response_package_protection_rule).to include( expect(mutation_response_package_protection_rule).to include(
'id' => be_present, 'id' => be_present,
'packageNamePattern' => kwargs[:package_name_pattern], 'packageNamePattern' => expected_attributes[:package_name_pattern],
'packageType' => kwargs[:package_type], 'packageType' => expected_attributes[:package_type].upcase,
'minimumAccessLevelForPush' => kwargs[:minimum_access_level_for_push] 'minimumAccessLevelForDelete' => expected_attributes[:minimum_access_level_for_delete]&.upcase,
'minimumAccessLevelForPush' => expected_attributes[:minimum_access_level_for_push]&.upcase
) )
end end
...@@ -65,9 +68,10 @@ ...@@ -65,9 +68,10 @@
expect(Packages::Protection::Rule.last).to have_attributes( expect(Packages::Protection::Rule.last).to have_attributes(
project: project, project: project,
package_name_pattern: kwargs[:package_name_pattern], package_name_pattern: expected_attributes[:package_name_pattern],
package_type: kwargs[:package_type].downcase, package_type: expected_attributes[:package_type]&.downcase,
minimum_access_level_for_push: kwargs[:minimum_access_level_for_push].downcase minimum_access_level_for_delete: expected_attributes[:minimum_access_level_for_delete]&.downcase,
minimum_access_level_for_push: expected_attributes[:minimum_access_level_for_push]&.downcase
) )
end end
end end
...@@ -78,7 +82,31 @@ ...@@ -78,7 +82,31 @@
end end
end end
it_behaves_like 'a successful response' it_behaves_like 'a successful response' do
let(:expected_attributes) { kwargs }
end
context 'when feature flag `packages_protected_packages_delete` is disabled' do
before do
stub_feature_flags(packages_protected_packages_delete: false)
end
it_behaves_like 'a successful response' do
let(:expected_attributes) { kwargs.merge(minimum_access_level_for_delete: nil) }
end
context 'when minimum_access_level_for_push is nil' do
let(:kwargs) { super().merge(minimum_access_level_for_push: nil) }
it_behaves_like 'an erroneous response'
it 'returns an error' do
subject
expect(mutation_response_errors).to include(/must have at least a minimum access role for push or delete./)
end
end
end
context 'with invalid kwargs leading to error from graphql' do context 'with invalid kwargs leading to error from graphql' do
let(:kwargs) do let(:kwargs) do
...@@ -109,6 +137,35 @@ ...@@ -109,6 +137,35 @@
end end
end end
context 'with invalid input fields `minimumAccessLevelForPush` and `minimumAccessLevelForDelete`' do
let(:kwargs) do
super().merge!(
minimum_access_level_for_push: 'INVALID_ACCESS_LEVEL',
minimum_access_level_for_delete: 'INVALID_ACCESS_LEVEL'
)
end
it_behaves_like 'an erroneous response'
it 'returns an error' do
subject
expect_graphql_errors_to_include([/minimumAccessLevelForPush/, /minimumAccessLevelForDelete/])
end
end
context 'with blank input fields `minimumAccessLevelForPush` and `minimumAccessLevelForDelete`' do
let(:kwargs) { super().merge(minimum_access_level_for_push: nil, minimum_access_level_for_delete: nil) }
it_behaves_like 'an erroneous response'
it 'returns an error' do
subject
expect(mutation_response_errors).to include('A rule must have at least a minimum access role for push or delete.')
end
end
context 'with existing packages protection rule' do context 'with existing packages protection rule' do
let_it_be(:existing_package_protection_rule) do let_it_be(:existing_package_protection_rule) do
create(:package_protection_rule, project: project, minimum_access_level_for_push: :maintainer) create(:package_protection_rule, project: project, minimum_access_level_for_push: :maintainer)
...@@ -128,14 +185,15 @@ ...@@ -128,14 +185,15 @@
super().merge!(package_name_pattern: "#{existing_package_protection_rule.package_name_pattern}-unique") super().merge!(package_name_pattern: "#{existing_package_protection_rule.package_name_pattern}-unique")
end end
it_behaves_like 'a successful response' it_behaves_like 'a successful response' do
let(:expected_attributes) { kwargs }
end
end end
context 'when field `minimum_access_level_for_push` is different than existing one' do context 'when field `minimum_access_level_for_push` is different than existing one' do
let(:kwargs) { super().merge!(minimum_access_level_for_push: 'MAINTAINER') } let(:kwargs) { super().merge!(minimum_access_level_for_push: 'MAINTAINER') }
it_behaves_like 'an erroneous response' it_behaves_like 'an erroneous response'
it 'returns an error' do it 'returns an error' do
subject.tap { expect(mutation_response_errors).to include(/Package name pattern has already been taken/) } subject.tap { expect(mutation_response_errors).to include(/Package name pattern has already been taken/) }
end end
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
'id' => package_protection_rule.to_global_id.to_s, 'id' => package_protection_rule.to_global_id.to_s,
'packageNamePattern' => package_protection_rule.package_name_pattern, 'packageNamePattern' => package_protection_rule.package_name_pattern,
'packageType' => package_protection_rule.package_type.upcase, 'packageType' => package_protection_rule.package_type.upcase,
'minimumAccessLevelForDelete' => package_protection_rule.minimum_access_level_for_delete.upcase,
'minimumAccessLevelForPush' => package_protection_rule.minimum_access_level_for_push.upcase 'minimumAccessLevelForPush' => package_protection_rule.minimum_access_level_for_push.upcase
} }
) )
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
<<~QUERY <<~QUERY
packageProtectionRule { packageProtectionRule {
packageNamePattern packageNamePattern
minimumAccessLevelForDelete
minimumAccessLevelForPush minimumAccessLevelForPush
} }
clientMutationId clientMutationId
...@@ -31,11 +32,13 @@ ...@@ -31,11 +32,13 @@
{ {
id: package_protection_rule.to_global_id, id: package_protection_rule.to_global_id,
package_name_pattern: "#{package_protection_rule.package_name_pattern}-updated", package_name_pattern: "#{package_protection_rule.package_name_pattern}-updated",
minimum_access_level_for_delete: 'ADMIN',
minimum_access_level_for_push: 'MAINTAINER' minimum_access_level_for_push: 'MAINTAINER'
} }
end end
let(:mutation_response) { graphql_mutation_response(:update_packages_protection_rule) } let(:mutation_response) { graphql_mutation_response(:update_packages_protection_rule) }
let(:mutation_response_errors) { mutation_response['errors'] }
subject { post_graphql_mutation(mutation, current_user: current_user) } subject { post_graphql_mutation(mutation, current_user: current_user) }
...@@ -47,19 +50,21 @@ ...@@ -47,19 +50,21 @@
expect(mutation_response).to include( expect(mutation_response).to include(
'packageProtectionRule' => { 'packageProtectionRule' => {
'packageNamePattern' => input[:package_name_pattern], 'packageNamePattern' => expected_attributes[:package_name_pattern],
'minimumAccessLevelForPush' => input[:minimum_access_level_for_push] 'minimumAccessLevelForDelete' => expected_attributes[:minimum_access_level_for_delete]&.upcase,
'minimumAccessLevelForPush' => expected_attributes[:minimum_access_level_for_push]&.upcase
} }
) )
end end
it do it 'updates attributes of existing package protection rule' do
subject.tap do expect { subject }.not_to change { ::Packages::Protection::Rule.count }
expect(package_protection_rule.reload).to have_attributes(
package_name_pattern: input[:package_name_pattern], expect(package_protection_rule.reload).to have_attributes(
minimum_access_level_for_push: input[:minimum_access_level_for_push].downcase package_name_pattern: expected_attributes[:package_name_pattern],
) minimum_access_level_for_delete: expected_attributes[:minimum_access_level_for_delete]&.downcase,
end minimum_access_level_for_push: expected_attributes[:minimum_access_level_for_push]&.downcase
)
end end
end end
...@@ -68,7 +73,33 @@ ...@@ -68,7 +73,33 @@
it { expect { subject }.not_to change { package_protection_rule.reload.updated_at } } it { expect { subject }.not_to change { package_protection_rule.reload.updated_at } }
end end
it_behaves_like 'a successful response' it_behaves_like 'a successful response' do
let(:expected_attributes) { input }
end
context 'when feature flag `packages_protected_packages_delete` is disabled' do
before do
package_protection_rule.update!(minimum_access_level_for_delete: nil)
stub_feature_flags(packages_protected_packages_delete: false)
end
it_behaves_like 'a successful response' do
let(:expected_attributes) do
input.merge(minimum_access_level_for_delete: package_protection_rule.minimum_access_level_for_delete)
end
end
context 'when minimum_access_level_for_push is nil' do
let(:input) { super().merge(minimum_access_level_for_push: nil) }
it 'includes error message in response' do
is_expected.tap do
expect(mutation_response_errors).to include(/at least a minimum access role for push or delete/)
end
end
end
end
context 'with other existing package protection rule with same package_name_pattern' do context 'with other existing package protection rule with same package_name_pattern' do
let_it_be_with_reload(:other_existing_package_protection_rule) do let_it_be_with_reload(:other_existing_package_protection_rule) do
...@@ -85,16 +116,20 @@ ...@@ -85,16 +116,20 @@
end end
it 'includes error message in response' do it 'includes error message in response' do
is_expected.tap { expect(mutation_response['errors']).to eq ['Package name pattern has already been taken'] } is_expected.tap { expect(mutation_response_errors).to eq ['Package name pattern has already been taken'] }
end end
end end
context 'with invalid input param `minimumAccessLevelForPush`' do context 'with invalid input param `minimumAccessLevelForPush`' do
let(:input) { super().merge(minimum_access_level_for_push: nil) } let(:input) { super().merge(minimum_access_level_for_push: 'INVALID_ACCESS_LEVEL') }
it_behaves_like 'an erroneous response' it { is_expected.tap { expect_graphql_errors_to_include(/invalid value for minimumAccessLevelForPush/) } }
end
context 'with invalid input param `minimumAccessLevelForDelete`' do
let(:input) { super().merge(minimum_access_level_for_delete: 'INVALID_ACCESS_LEVEL') }
it { is_expected.tap { expect_graphql_errors_to_include(/minimumAccessLevelForPush can't be blank/) } } it { is_expected.tap { expect_graphql_errors_to_include(/invalid value for minimumAccessLevelForDelete/) } }
end end
context 'with invalid input param `packageNamePattern`' do context 'with invalid input param `packageNamePattern`' do
...@@ -105,6 +140,16 @@ ...@@ -105,6 +140,16 @@
it { is_expected.tap { expect_graphql_errors_to_include(/packageNamePattern can't be blank/) } } it { is_expected.tap { expect_graphql_errors_to_include(/packageNamePattern can't be blank/) } }
end end
context 'with blank input fields `minimumAccessLevelForPush` and `minimumAccessLevelForDelete`' do
let(:input) { super().merge(minimum_access_level_for_push: nil, minimum_access_level_for_delete: nil) }
it 'includes error message in response' do
is_expected.tap do
expect(mutation_response_errors).to include(/at least a minimum access role for push or delete/)
end
end
end
context 'when current_user does not have permission' do context 'when current_user does not have permission' do
let_it_be(:developer) { create(:user, developer_of: project) } let_it_be(:developer) { create(:user, developer_of: project) }
let_it_be(:reporter) { create(:user, reporter_of: project) } let_it_be(:reporter) { create(:user, reporter_of: project) }
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
hash_including( hash_including(
'packageNamePattern' => package_protection_rule.package_name_pattern, 'packageNamePattern' => package_protection_rule.package_name_pattern,
'packageType' => 'NPM', 'packageType' => 'NPM',
'minimumAccessLevelForDelete' => 'OWNER',
'minimumAccessLevelForPush' => 'MAINTAINER' 'minimumAccessLevelForPush' => 'MAINTAINER'
) )
) )
......
...@@ -61,6 +61,13 @@ ...@@ -61,6 +61,13 @@
it_behaves_like 'an erroneous service response with side effect', it_behaves_like 'an erroneous service response with side effect',
message: "'unknown_package_type' is not a valid package_type" message: "'unknown_package_type' is not a valid package_type"
end end
context 'when minimum_access_level_for_delete and minimum_access_level_for_push are blank' do
let(:params) { super().merge(minimum_access_level_for_delete: nil, minimum_access_level_for_push: nil) }
it_behaves_like 'an erroneous service response with side effect',
message: ['A rule must have at least a minimum access role for push or delete.']
end
end end
context 'with existing PackageProtectionRule' do context 'with existing PackageProtectionRule' do
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
:package_protection_rule, :package_protection_rule,
package_name_pattern: "#{package_protection_rule.package_name_pattern}-updated", package_name_pattern: "#{package_protection_rule.package_name_pattern}-updated",
package_type: 'npm', package_type: 'npm',
minimum_access_level_for_push: 'owner', minimum_access_level_for_delete: 'admin',
minimum_access_level_for_delete: 'owner' minimum_access_level_for_push: 'owner'
) )
end end
...@@ -72,6 +72,13 @@ ...@@ -72,6 +72,13 @@
message: "'unknown_package_type' is not a valid package_type" message: "'unknown_package_type' is not a valid package_type"
end end
context 'when minimum_access_level_for_delete and minimum_access_level_for_push are blank' do
let(:params) { super().merge(minimum_access_level_for_delete: nil, minimum_access_level_for_push: nil) }
it_behaves_like 'an erroneous service response with side effect',
message: ['A rule must have at least a minimum access role for push or delete.']
end
context 'with empty params' do context 'with empty params' do
let(:params) { {} } let(:params) { {} }
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册