From a9b94e02c9d0e9722d767b4aeeeada54b7ada0df Mon Sep 17 00:00:00 2001
From: Hunter Stewart <hustewart@gitlab.com>
Date: Thu, 4 Apr 2024 17:00:26 +0000
Subject: [PATCH] Add canApprove field to ApprovalRuleForSummaryType

The functionality is already in Authorizable. This commit exposes
whether or not the current_user in the graphQL request is permitted to
deploy per rule.
---
 doc/api/graphql/reference/index.md            |  1 +
 .../approval_rule_for_summary_type.rb         |  8 ++++
 .../approval_rule_for_summary_type_spec.rb    | 40 ++++++++++++++++++-
 .../approval_rule_spec.rb                     | 18 ++-------
 .../protected_environments/approval_rule.rb   | 28 +++++++++++++
 5 files changed, 78 insertions(+), 17 deletions(-)
 create mode 100644 spec/support/shared_contexts/models/protected_environments/approval_rule.rb

diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 6aae76d335a0..f8d415038aea 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -27561,6 +27561,7 @@ Which group, user or role is allowed to approve deployments to the environment.
 | <a id="protectedenvironmentapprovalruleforsummaryaccesslevel"></a>`accessLevel` | [`AccessLevel`](#accesslevel) | Role details. Present if it's role specific access control. |
 | <a id="protectedenvironmentapprovalruleforsummaryapprovals"></a>`approvals` | [`[DeploymentApproval!]`](#deploymentapproval) | Current approvals of the deployment. |
 | <a id="protectedenvironmentapprovalruleforsummaryapprovedcount"></a>`approvedCount` | [`Int`](#int) | Approved count. |
+| <a id="protectedenvironmentapprovalruleforsummarycanapprove"></a>`canApprove` | [`Boolean`](#boolean) | Indicates whether a user is authorized to approve. |
 | <a id="protectedenvironmentapprovalruleforsummarygroup"></a>`group` | [`Group`](#group) | Group details. Present if it's group specific access control. |
 | <a id="protectedenvironmentapprovalruleforsummarypendingapprovalcount"></a>`pendingApprovalCount` | [`Int`](#int) | Pending approval count. |
 | <a id="protectedenvironmentapprovalruleforsummaryrequiredapprovals"></a>`requiredApprovals` | [`Int`](#int) | Number of required approvals. |
diff --git a/ee/app/graphql/types/protected_environments/approval_rule_for_summary_type.rb b/ee/app/graphql/types/protected_environments/approval_rule_for_summary_type.rb
index be01e9bc8cd0..5ad74e7da8f7 100644
--- a/ee/app/graphql/types/protected_environments/approval_rule_for_summary_type.rb
+++ b/ee/app/graphql/types/protected_environments/approval_rule_for_summary_type.rb
@@ -24,6 +24,14 @@ class ApprovalRuleForSummaryType < ApprovalRuleType
             type: [::Types::Deployments::ApprovalType],
             description: 'Current approvals of the deployment.',
             method: :approvals_for_summary
+
+      field :can_approve,
+            type: GraphQL::Types::Boolean,
+            description: 'Indicates whether a user is authorized to approve.'
+
+      def can_approve
+        object.check_access(current_user)
+      end
     end
     # rubocop:enable Graphql/AuthorizeTypes
   end
diff --git a/ee/spec/graphql/types/protected_environments/approval_rule_for_summary_type_spec.rb b/ee/spec/graphql/types/protected_environments/approval_rule_for_summary_type_spec.rb
index 70ad98f755e1..eb166c6db8ff 100644
--- a/ee/spec/graphql/types/protected_environments/approval_rule_for_summary_type_spec.rb
+++ b/ee/spec/graphql/types/protected_environments/approval_rule_for_summary_type_spec.rb
@@ -2,14 +2,50 @@
 
 require 'spec_helper'
 
-RSpec.describe GitlabSchema.types['ProtectedEnvironmentApprovalRuleForSummary'] do
+RSpec.describe GitlabSchema.types['ProtectedEnvironmentApprovalRuleForSummary'],
+  feature_category: :deployment_management do
   specify { expect(described_class.graphql_name).to eq('ProtectedEnvironmentApprovalRuleForSummary') }
 
   it 'includes the expected fields' do
     expected_fields = %w[
-      approved_count pending_approval_count required_approvals status approvals
+      approvals
+      approved_count
+      can_approve
+      pending_approval_count
+      required_approvals
+      status
     ]
 
     expect(described_class).to include_graphql_fields(*expected_fields)
   end
+
+  describe '#can_approve' do
+    include_context 'with an approval rule and approver'
+
+    subject(:result) do
+      described_class.send(
+        :new, approval_rule, { current_user: user_being_checked }
+      ).can_approve
+    end
+
+    context 'when user can approve' do
+      let_it_be(:user_being_checked) { approver }
+
+      it { is_expected.to eq(true) }
+    end
+
+    context 'when user cannot approve' do
+      include_context 'with a non approver'
+
+      let_it_be(:user_being_checked) { non_approver }
+
+      it { is_expected.to eq(false) }
+    end
+
+    context 'when user is nil' do
+      let_it_be(:user_being_checked) { nil }
+
+      it { is_expected.to eq(false) }
+    end
+  end
 end
diff --git a/ee/spec/models/protected_environments/approval_rule_spec.rb b/ee/spec/models/protected_environments/approval_rule_spec.rb
index 36a23c752343..852cc5fc1d44 100644
--- a/ee/spec/models/protected_environments/approval_rule_spec.rb
+++ b/ee/spec/models/protected_environments/approval_rule_spec.rb
@@ -1,21 +1,9 @@
 # frozen_string_literal: true
 require 'spec_helper'
 
-RSpec.describe ProtectedEnvironments::ApprovalRule do
-  let_it_be(:project) { create(:project, :repository) }
-  let_it_be(:environment) { create(:environment, project: project) }
-  let_it_be(:approver) { create(:user) }
-
-  let!(:protected_environment) { create(:protected_environment, project: project, name: environment.name) }
-
-  let!(:approval_rule) do
-    create(
-      :protected_environment_approval_rule,
-      protected_environment: protected_environment,
-      user: approver,
-      required_approvals: 1
-    )
-  end
+RSpec.describe ProtectedEnvironments::ApprovalRule,
+  feature_category: :deployment_management do
+  include_context 'with an approval rule and approver'
 
   describe 'associations' do
     it { is_expected.to have_many(:deployment_approvals).class_name('Deployments::Approval').inverse_of(:approval_rule) }
diff --git a/spec/support/shared_contexts/models/protected_environments/approval_rule.rb b/spec/support/shared_contexts/models/protected_environments/approval_rule.rb
new file mode 100644
index 000000000000..76f4d4380da6
--- /dev/null
+++ b/spec/support/shared_contexts/models/protected_environments/approval_rule.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with an approval rule and approver' do
+  let_it_be(:project) { create(:project, :repository) }
+  let_it_be(:environment) { create(:environment, project: project) }
+  let_it_be(:approver) { create(:user) }
+
+  let!(:protected_environment) do
+    create(
+      :protected_environment,
+      project: project,
+      name: environment.name
+    )
+  end
+
+  let!(:approval_rule) do
+    create(
+      :protected_environment_approval_rule,
+      protected_environment: protected_environment,
+      user: approver,
+      required_approvals: 1
+    )
+  end
+end
+
+RSpec.shared_context 'with a non approver' do
+  let_it_be(:non_approver) { create(:user) }
+end
-- 
GitLab