Commit dcb03526 authored by Alex Kalderimis's avatar Alex Kalderimis

Merge branch '323116-add-approval-rules-to-merge-request-graphql-api' into 'master'

Add approvalRules to MergeRequest GraphQL API

See merge request gitlab-org/gitlab!68502
parents 1ca3e5d9 8339099b
......@@ -2113,6 +2113,7 @@ Gitlab/NamespacedClass:
- 'ee/app/models/weight_note.rb'
- 'ee/app/policies/approval_merge_request_rule_policy.rb'
- 'ee/app/policies/approval_project_rule_policy.rb'
- 'ee/app/policies/approval_state_policy.rb'
- 'ee/app/policies/dast_scanner_profile_policy.rb'
- 'ee/app/policies/dast_site_profile_policy.rb'
- 'ee/app/policies/dast_site_validation_policy.rb'
......
......@@ -7568,9 +7568,19 @@ Describes a rule for who can approve merge requests.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="approvalruleapprovalsrequired"></a>`approvalsRequired` | [`Int`](#int) | Number of required approvals. |
| <a id="approvalruleapproved"></a>`approved` | [`Boolean`](#boolean) | Indicates if the rule is satisfied. |
| <a id="approvalruleapprovedby"></a>`approvedBy` | [`UserCoreConnection`](#usercoreconnection) | List of users defined in the rule that approved the merge request. (see [Connections](#connections)) |
| <a id="approvalrulecontainshiddengroups"></a>`containsHiddenGroups` | [`Boolean`](#boolean) | Indicates if the rule contains approvers from a hidden group. |
| <a id="approvalruleeligibleapprovers"></a>`eligibleApprovers` | [`UserCoreConnection`](#usercoreconnection) | List of all users eligible to approve the merge request (defined explicitly and from associated groups). (see [Connections](#connections)) |
| <a id="approvalrulegroups"></a>`groups` | [`GroupConnection`](#groupconnection) | List of groups added as approvers for the rule. (see [Connections](#connections)) |
| <a id="approvalruleid"></a>`id` | [`GlobalID!`](#globalid) | ID of the rule. |
| <a id="approvalrulename"></a>`name` | [`String`](#string) | Name of the rule. |
| <a id="approvalruleoverridden"></a>`overridden` | [`Boolean`](#boolean) | Indicates if the rule was overridden for the merge request. |
| <a id="approvalrulesection"></a>`section` | [`String`](#string) | Named section of the Code Owners file that the rule applies to. |
| <a id="approvalrulesourcerule"></a>`sourceRule` | [`ApprovalRule`](#approvalrule) | Source rule used to create the rule. |
| <a id="approvalruletype"></a>`type` | [`ApprovalRuleType`](#approvalruletype) | Type of the rule. |
| <a id="approvalruleusers"></a>`users` | [`UserCoreConnection`](#usercoreconnection) | List of users added as approvers for the rule. (see [Connections](#connections)) |
### `AwardEmoji`
......@@ -10600,6 +10610,7 @@ Maven metadata.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mergerequestallowcollaboration"></a>`allowCollaboration` | [`Boolean`](#boolean) | Indicates if members of the target project can push to the fork. |
| <a id="mergerequestapprovalstate"></a>`approvalState` | [`MergeRequestApprovalState!`](#mergerequestapprovalstate) | Information relating to rules that must be satisfied to merge this merge request. |
| <a id="mergerequestapprovalsleft"></a>`approvalsLeft` | [`Int`](#int) | Number of approvals left. |
| <a id="mergerequestapprovalsrequired"></a>`approvalsRequired` | [`Int`](#int) | Number of approvals required. |
| <a id="mergerequestapproved"></a>`approved` | [`Boolean!`](#boolean) | Indicates if the merge request has all the required approvals. Returns true if no required approvals are configured. |
......@@ -10746,6 +10757,17 @@ Returns [`String!`](#string).
| ---- | ---- | ----------- |
| <a id="mergerequestreferencefull"></a>`full` | [`Boolean`](#boolean) | Boolean option specifying whether the reference should be returned in full. |
### `MergeRequestApprovalState`
Information relating to rules that must be satisfied to merge this merge request.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mergerequestapprovalstateapprovalrulesoverwritten"></a>`approvalRulesOverwritten` | [`Boolean`](#boolean) | Indicates if the merge request approval rules are overwritten for the merge request. |
| <a id="mergerequestapprovalstaterules"></a>`rules` | [`[ApprovalRule!]`](#approvalrule) | List of approval rules associated with the merge request. |
### `MergeRequestAssignee`
A user assigned to a merge request.
......
......@@ -23,6 +23,8 @@ module EE
null: false, calls_gitaly: true,
method: :security_reports_up_to_date?,
description: 'Indicates if the target branch security reports are out of date.'
field :approval_state, ::Types::MergeRequests::ApprovalStateType, null: false,
description: 'Information relating to rules that must be satisfied to merge this merge request.'
end
def merge_trains_count
......
......@@ -6,6 +6,8 @@ module Types
description 'Describes a rule for who can approve merge requests.'
authorize :read_approval_rule
present_using ::ApprovalRulePresenter
field :id,
type: ::Types::GlobalIDType,
null: false,
......@@ -21,5 +23,60 @@ module Types
null: true,
method: :rule_type,
description: 'Type of the rule.'
field :approvals_required,
type: GraphQL::Types::Int,
null: true,
description: 'Number of required approvals.'
field :approved,
type: GraphQL::Types::Boolean,
method: :approved?,
null: true,
description: 'Indicates if the rule is satisfied.'
field :overridden,
type: GraphQL::Types::Boolean,
method: :overridden?,
null: true,
description: 'Indicates if the rule was overridden for the merge request.'
field :section,
type: GraphQL::Types::String,
null: true,
description: 'Named section of the Code Owners file that the rule applies to.'
field :contains_hidden_groups,
type: GraphQL::Types::Boolean,
method: :contains_hidden_groups?,
null: true,
description: 'Indicates if the rule contains approvers from a hidden group.'
field :source_rule,
type: self,
null: true,
description: 'Source rule used to create the rule.'
field :eligible_approvers,
type: ::Types::UserType.connection_type,
method: :approvers,
null: true,
description: 'List of all users eligible to approve the merge request (defined explicitly and from associated groups).'
field :users,
type: ::Types::UserType.connection_type,
null: true,
description: 'List of users added as approvers for the rule.'
field :approved_by,
type: ::Types::UserType.connection_type,
method: :approved_approvers,
null: true,
description: 'List of users defined in the rule that approved the merge request.'
field :groups,
type: ::Types::GroupType.connection_type,
null: true,
description: 'List of groups added as approvers for the rule.'
end
end
# frozen_string_literal: true
module Types
module MergeRequests
class ApprovalStateType < BaseObject
graphql_name 'MergeRequestApprovalState'
description 'Information relating to rules that must be satisfied to merge this merge request.'
authorize :read_merge_request
field :approval_rules_overwritten, GraphQL::Types::Boolean, method: :approval_rules_overwritten?,
description: 'Indicates if the merge request approval rules are overwritten for the merge request.', null: true
field :rules, [::Types::ApprovalRuleType], method: :wrapped_approval_rules,
description: 'List of approval rules associated with the merge request.', null: true, complexity: 5
end
end
end
# frozen_string_literal: true
class ApprovalStatePolicy < BasePolicy
delegate :project
end
......@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['MergeRequest'] do
it { expect(described_class).to have_graphql_fields(:approvals_required, :merge_trains_count).at_least }
it { expect(described_class).to have_graphql_fields(:approvals_required, :merge_trains_count, :approval_state).at_least }
it { expect(described_class).to have_graphql_field(:approved, complexity: 2, calls_gitaly?: true) }
it { expect(described_class).to have_graphql_field(:approvals_left, complexity: 2, calls_gitaly?: true) }
it { expect(described_class).to have_graphql_field(:has_security_reports, calls_gitaly?: true) }
......
......@@ -3,7 +3,12 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['ApprovalRule'] do
let(:fields) { %i[id name type] }
let(:fields) do
%i[
id name type approvals_required approved overridden section contains_hidden_groups source_rule
eligible_approvers users approved_by groups section
]
end
it { expect(described_class).to have_graphql_fields(fields) }
it { expect(described_class).to require_graphql_authorizations(:read_approval_rule) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['MergeRequestApprovalState'] do
let(:fields) { %i[approval_rules_overwritten rules] }
it { expect(described_class).to have_graphql_fields(fields) }
it { expect(described_class).to require_graphql_authorizations(:read_merge_request) }
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ApprovalStatePolicy do
let!(:project) { create(:project) }
let!(:user) { create(:user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let!(:approval_state) { ApprovalState.new(merge_request) }
subject(:policy) { described_class.new(user, approval_state) }
context 'when user does not have access to project' do
it { is_expected.to be_disallowed(:read_merge_request) }
end
context 'when user does have access to project' do
before do
project.add_developer(user)
end
it { is_expected.to be_allowed(:read_merge_request) }
end
end
......@@ -61,7 +61,13 @@ RSpec.describe 'MergeRequestReviewer' do
the_rule = eq(
'id' => global_id_of(rule),
'name' => rule.name,
'type' => 'CODE_OWNER'
'type' => 'CODE_OWNER',
'approvalsRequired' => 0,
'approved' => true,
'containsHiddenGroups' => false,
'overridden' => false,
'section' => 'codeowners',
'sourceRule' => nil
)
post_graphql(query)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Query.project.mergeRequest.approvalState' do
include GraphqlHelpers
let_it_be_with_refind(:current_user) { create(:user) }
context 'when requesting information about approval state' do
let_it_be_with_refind(:user) { create(:user) }
let_it_be_with_refind(:group) { create(:group) }
let_it_be_with_refind(:project) { create(:project, :public, :repository, group: group) }
let_it_be_with_refind(:merge_request) { create(:merge_request, source_project: project) }
let_it_be_with_refind(:fields) do
<<~QUERY
approvalState {
approvalRulesOverwritten
rules {
id
name
type
approvalsRequired
approved
containsHiddenGroups
overridden
section
}
}
QUERY
end
let(:query) do
graphql_query_for(
:project,
{ full_path: project.full_path },
query_graphql_field(
:merge_request,
{ iid: merge_request.iid.to_s },
fields
)
)
end
let(:approval_state) do
graphql_data_at(:project,
:merge_request,
:approval_state)
end
before do
merge_request.reviewers << user
end
context 'when no approval rule is set to the MR' do
it 'returns null data' do
post_graphql(query)
expect(approval_state).to eq('approvalRulesOverwritten' => false, 'rules' => [])
end
end
context 'when the MR has approval rules configured' do
let(:code_owner_rule) { create(:code_owner_rule, merge_request: merge_request) }
before do
stub_licensed_features(merge_request_approvers: true)
code_owner_rule.users << user
end
it 'returns appropriate data' do
post_graphql(query)
expect(approval_state).to eq({
'approvalRulesOverwritten' => false,
'rules' => [{
'approvalsRequired' => 0,
'approved' => true,
'containsHiddenGroups' => false,
'id' => global_id_of(code_owner_rule),
'name' => code_owner_rule.name,
'overridden' => false,
'section' => 'codeowners',
'type' => 'CODE_OWNER'
}]
})
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment