From e17beec135610db9f9211087fd48547531f9ea2a Mon Sep 17 00:00:00 2001 From: Gerardo Date: Sat, 2 Mar 2024 19:23:13 +0100 Subject: [PATCH 1/2] Protected containers: GraphQL query for container protection rules - Adding new graphql endpoint for querying container protection rules of a specific project - Consider feature flag `:container_registry_protected_containers` Changelog: added --- ...iner_registry_protection_rules_resolver.rb | 15 ++++ app/graphql/types/project_type.rb | 6 ++ doc/api/graphql/reference/index.md | 24 ++++++ ...ontainer_registry_protection_rules_spec.rb | 86 +++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 app/graphql/resolvers/project_container_registry_protection_rules_resolver.rb create mode 100644 spec/requests/api/graphql/project/container_registry_protection_rules_spec.rb diff --git a/app/graphql/resolvers/project_container_registry_protection_rules_resolver.rb b/app/graphql/resolvers/project_container_registry_protection_rules_resolver.rb new file mode 100644 index 00000000000000..e051943ee5b96e --- /dev/null +++ b/app/graphql/resolvers/project_container_registry_protection_rules_resolver.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Resolvers + class ProjectContainerRegistryProtectionRulesResolver < BaseResolver + type Types::ContainerRegistry::Protection::RuleType.connection_type, null: true + + alias_method :project, :object + + def resolve(**_args) + return [] if Feature.disabled?(:container_registry_protected_containers, project) + + project.container_registry_protection_rules + end + end +end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 4ee6e2429b7e4d..76bea55375c03d 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -496,6 +496,12 @@ class ProjectType < BaseObject null: true, description: 'Container expiration policy of the project.' + field :container_registry_protection_rules, + Types::ContainerRegistry::Protection::RuleType.connection_type, + null: true, + description: 'Container protection rules for the project.', + resolver: Resolvers::ProjectContainerRegistryProtectionRulesResolver + field :container_repositories, Types::ContainerRepositoryType.connection_type, null: true, description: 'Container repositories of the project.', diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index e90412c9e84e8d..ca6f3c13c4f88c 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -10450,6 +10450,29 @@ The edge type for [`ConnectedAgent`](#connectedagent). | `cursor` | [`String!`](#string) | A cursor for use in pagination. | | `node` | [`ConnectedAgent`](#connectedagent) | The item at the end of the edge. | +#### `ContainerRegistryProtectionRuleConnection` + +The connection type for [`ContainerRegistryProtectionRule`](#containerregistryprotectionrule). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `edges` | [`[ContainerRegistryProtectionRuleEdge]`](#containerregistryprotectionruleedge) | A list of edges. | +| `nodes` | [`[ContainerRegistryProtectionRule]`](#containerregistryprotectionrule) | A list of nodes. | +| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `ContainerRegistryProtectionRuleEdge` + +The edge type for [`ContainerRegistryProtectionRule`](#containerregistryprotectionrule). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `cursor` | [`String!`](#string) | A cursor for use in pagination. | +| `node` | [`ContainerRegistryProtectionRule`](#containerregistryprotectionrule) | The item at the end of the edge. | + #### `ContainerRepositoryConnection` The connection type for [`ContainerRepository`](#containerrepository). @@ -25268,6 +25291,7 @@ Check permissions for the current user on a vulnerability finding. | `complianceFrameworks` | [`ComplianceFrameworkConnection`](#complianceframeworkconnection) | Compliance frameworks associated with the project. (see [Connections](#connections)) | | `containerExpirationPolicy` | [`ContainerExpirationPolicy`](#containerexpirationpolicy) | Container expiration policy of the project. | | `containerRegistryEnabled` | [`Boolean`](#boolean) | Indicates if Container Registry is enabled for the current user. | +| `containerRegistryProtectionRules` | [`ContainerRegistryProtectionRuleConnection`](#containerregistryprotectionruleconnection) | Container protection rules for the project. (see [Connections](#connections)) | | `containerRepositoriesCount` | [`Int!`](#int) | Number of container repositories in the project. | | `corpuses` | [`CoverageFuzzingCorpusConnection`](#coveragefuzzingcorpusconnection) | Find corpuses of the project. (see [Connections](#connections)) | | `createdAt` | [`Time`](#time) | Timestamp of the project creation. | diff --git a/spec/requests/api/graphql/project/container_registry_protection_rules_spec.rb b/spec/requests/api/graphql/project/container_registry_protection_rules_spec.rb new file mode 100644 index 00000000000000..57b54f6c71cf32 --- /dev/null +++ b/spec/requests/api/graphql/project/container_registry_protection_rules_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting the containers protection rules linked to a project', :aggregate_failures, feature_category: :container_registry do + include GraphqlHelpers + + let_it_be_with_reload(:project) { create(:project) } + let_it_be(:user) { project.owner } + + let(:query) do + graphql_query_for( + :project, + { full_path: project.full_path }, + query_nodes(:containerRegistryProtectionRules, of: 'ContainerRegistryProtectionRule') + ) + end + + let(:protection_rules) { graphql_data_at(:project, :containerRegistryProtectionRules, :nodes) } + + subject(:send_graqhql_query) { post_graphql(query, current_user: user) } + + context 'with authorized user owner' do + before do + send_graqhql_query + end + + context 'with container protection rule' do + let_it_be(:container_protection_rule) { create(:container_registry_protection_rule, project: project) } + + it_behaves_like 'a working graphql query' + + it 'returns only on containersProtectionRule' do + expect(protection_rules.count).to eq 1 + end + + it 'returns all containers protection rule fields' do + expect(protection_rules).to include( + hash_including( + 'repositoryPathPattern' => container_protection_rule.repository_path_pattern, + 'pushProtectedUpToAccessLevel' => 'DEVELOPER', + 'deleteProtectedUpToAccessLevel' => 'DEVELOPER' + ) + ) + end + end + + context 'without container protection rule' do + it_behaves_like 'a working graphql query' + + it 'returns no containersProtectionRule' do + expect(protection_rules).to eq [] + end + end + end + + context 'with unauthorized user' do + let_it_be(:user) { create(:user).tap { |u| project.add_developer(u) } } + + before do + send_graqhql_query + end + + it_behaves_like 'a working graphql query' + + it 'returns no container protection rules' do + expect(protection_rules).to eq [] + end + end + + context "when feature flag ':containers_protected_containers' disabled" do + let_it_be(:container_protection_rule) { create(:container_registry_protection_rule, project: project) } + + before do + stub_feature_flags(container_registry_protected_containers: false) + + send_graqhql_query + end + + it_behaves_like 'a working graphql query' + + it 'returns no container protection rules' do + expect(protection_rules).to eq [] + end + end +end -- GitLab From 6f6d8f45d4e05c5a7143159ff14224d5db8bdcef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarka=20Ko=C5=A1anov=C3=A1?= Date: Thu, 7 Mar 2024 16:38:23 +0000 Subject: [PATCH 2/2] refactor: Apply suggestion from @jarka --- app/graphql/types/project_type.rb | 1 + doc/api/graphql/reference/index.md | 2 +- .../project/container_registry_protection_rules_spec.rb | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 76bea55375c03d..05add3af1819e8 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -500,6 +500,7 @@ class ProjectType < BaseObject Types::ContainerRegistry::Protection::RuleType.connection_type, null: true, description: 'Container protection rules for the project.', + alpha: { milestone: '16.10' }, resolver: Resolvers::ProjectContainerRegistryProtectionRulesResolver field :container_repositories, Types::ContainerRepositoryType.connection_type, diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index ca6f3c13c4f88c..722deb02095612 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -25291,7 +25291,7 @@ Check permissions for the current user on a vulnerability finding. | `complianceFrameworks` | [`ComplianceFrameworkConnection`](#complianceframeworkconnection) | Compliance frameworks associated with the project. (see [Connections](#connections)) | | `containerExpirationPolicy` | [`ContainerExpirationPolicy`](#containerexpirationpolicy) | Container expiration policy of the project. | | `containerRegistryEnabled` | [`Boolean`](#boolean) | Indicates if Container Registry is enabled for the current user. | -| `containerRegistryProtectionRules` | [`ContainerRegistryProtectionRuleConnection`](#containerregistryprotectionruleconnection) | Container protection rules for the project. (see [Connections](#connections)) | +| `containerRegistryProtectionRules` **{warning-solid}** | [`ContainerRegistryProtectionRuleConnection`](#containerregistryprotectionruleconnection) | **Introduced** in GitLab 16.10. **Status**: Experiment. Container protection rules for the project. | | `containerRepositoriesCount` | [`Int!`](#int) | Number of container repositories in the project. | | `corpuses` | [`CoverageFuzzingCorpusConnection`](#coveragefuzzingcorpusconnection) | Find corpuses of the project. (see [Connections](#connections)) | | `createdAt` | [`Time`](#time) | Timestamp of the project creation. | diff --git a/spec/requests/api/graphql/project/container_registry_protection_rules_spec.rb b/spec/requests/api/graphql/project/container_registry_protection_rules_spec.rb index 57b54f6c71cf32..57c50e4c33bc24 100644 --- a/spec/requests/api/graphql/project/container_registry_protection_rules_spec.rb +++ b/spec/requests/api/graphql/project/container_registry_protection_rules_spec.rb @@ -49,7 +49,7 @@ it_behaves_like 'a working graphql query' it 'returns no containersProtectionRule' do - expect(protection_rules).to eq [] + expect(protection_rules).to be_empty end end end @@ -64,7 +64,7 @@ it_behaves_like 'a working graphql query' it 'returns no container protection rules' do - expect(protection_rules).to eq [] + expect(protection_rules).to be_empty end end @@ -80,7 +80,7 @@ it_behaves_like 'a working graphql query' it 'returns no container protection rules' do - expect(protection_rules).to eq [] + expect(protection_rules).to be_empty end end end -- GitLab