Commit 0aaaa397 authored by Igor Drozdov's avatar Igor Drozdov

Merge branch 'sh-fix-any-approvals-with-project-rules' into 'master'

Allow overriding of project approvers in merge request

See merge request gitlab-org/gitlab!34026
parents 5e090e9c dcf38617
---
title: Fix approval rule type when project rule has users/groups
merge_request: 34026
author:
type: fixed
...@@ -7,6 +7,7 @@ module ApprovalRules ...@@ -7,6 +7,7 @@ module ApprovalRules
# @param target [Project, MergeRequest] # @param target [Project, MergeRequest]
def initialize(target, user, params) def initialize(target, user, params)
@rule = target.approval_rules.build @rule = target.approval_rules.build
@params = params
# report_approver rule_type is currently auto-set according to rulename # report_approver rule_type is currently auto-set according to rulename
# Techdebt to be addressed with: https://gitlab.com/gitlab-org/gitlab/issues/12759 # Techdebt to be addressed with: https://gitlab.com/gitlab-org/gitlab/issues/12759
...@@ -14,8 +15,10 @@ module ApprovalRules ...@@ -14,8 +15,10 @@ module ApprovalRules
params.reverse_merge!(rule_type: :report_approver) params.reverse_merge!(rule_type: :report_approver)
end end
handle_any_approver_rule_creation(target, @rule.project, params) # If merge request approvers are specified, they take precedence over project
# approvers.
copy_approval_project_rule_properties(params) if target.is_a?(MergeRequest) copy_approval_project_rule_properties(params) if target.is_a?(MergeRequest)
handle_any_approver_rule_creation(target, @rule.project, params)
super(@rule.project, user, params) super(@rule.project, user, params)
end end
...@@ -29,17 +32,16 @@ module ApprovalRules ...@@ -29,17 +32,16 @@ module ApprovalRules
return if approval_project_rule.blank? return if approval_project_rule.blank?
# Remove the following from params so when set they'll be ignored
params.delete(:user_ids)
params.delete(:group_ids)
params[:name] = approval_project_rule.name params[:name] = approval_project_rule.name
params[:users] = approval_project_rule.users
params[:groups] = approval_project_rule.groups unless approvers_set?
params[:users] = approval_project_rule.users
params[:groups] = approval_project_rule.groups
end
end end
def handle_any_approver_rule_creation(target, project, params) def handle_any_approver_rule_creation(target, project, params)
if params[:user_ids].blank? && params[:group_ids].blank? unless approvers_present?
params.reverse_merge!(rule_type: :any_approver, name: ApprovalRuleLike::ALL_MEMBERS) params.reverse_merge!(rule_type: :any_approver, name: ApprovalRuleLike::ALL_MEMBERS)
return return
...@@ -49,5 +51,13 @@ module ApprovalRules ...@@ -49,5 +51,13 @@ module ApprovalRules
target.approval_rules.any_approver.delete_all target.approval_rules.any_approver.delete_all
end end
def approvers_set?
@params.key?(:user_ids) || @params.key?(:group_ids)
end
def approvers_present?
%i(user_ids group_ids users groups).any? { |key| @params[key].present? }
end
end end
end end
...@@ -146,6 +146,12 @@ RSpec.describe API::MergeRequestApprovalRules do ...@@ -146,6 +146,12 @@ RSpec.describe API::MergeRequestApprovalRules do
let(:approver) { create(:user) } let(:approver) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:approval_project_rule_id) { nil } let(:approval_project_rule_id) { nil }
let(:approver_params) do
{
user_ids: user_ids,
group_ids: group_ids
}
end
let(:user_ids) { [] } let(:user_ids) { [] }
let(:group_ids) { [] } let(:group_ids) { [] }
...@@ -153,10 +159,8 @@ RSpec.describe API::MergeRequestApprovalRules do ...@@ -153,10 +159,8 @@ RSpec.describe API::MergeRequestApprovalRules do
{ {
name: 'Test', name: 'Test',
approvals_required: 1, approvals_required: 1,
approval_project_rule_id: approval_project_rule_id, approval_project_rule_id: approval_project_rule_id
user_ids: user_ids, }.merge(approver_params)
group_ids: group_ids
}
end end
let(:action) { post api(url, current_user), params: params } let(:action) { post api(url, current_user), params: params }
...@@ -222,15 +226,32 @@ RSpec.describe API::MergeRequestApprovalRules do ...@@ -222,15 +226,32 @@ RSpec.describe API::MergeRequestApprovalRules do
let(:approval_project_rule_id) { approval_project_rule.id } let(:approval_project_rule_id) { approval_project_rule.id }
it 'copies the attributes from the project rule execpt approvals_required' do context 'with blank approver params' do
rule = json_response it 'copies the attributes from the project rule except approvers' do
rule = json_response
expect(rule['name']).to eq(approval_project_rule.name) expect(rule['name']).to eq(approval_project_rule.name)
expect(rule['approvals_required']).to eq(params[:approvals_required]) expect(rule['approvals_required']).to eq(params[:approvals_required])
expect(rule['source_rule']['approvals_required']).to eq(approval_project_rule.approvals_required) expect(rule['source_rule']['approvals_required']).to eq(approval_project_rule.approvals_required)
expect(rule['eligible_approvers']).to match([hash_including('id' => approver.id)]) expect(rule['eligible_approvers']).to eq([])
expect(rule['users']).to match([hash_including('id' => approver.id)]) expect(rule['users']).to eq([])
expect(rule['groups']).to match([hash_including('id' => group.id)]) expect(rule['groups']).to eq([])
end
end
context 'with omitted approver params' do
let(:approver_params) { {} }
it 'copies the attributes from the project rule except approvals_required' do
rule = json_response
expect(rule['name']).to eq(approval_project_rule.name)
expect(rule['approvals_required']).to eq(params[:approvals_required])
expect(rule['source_rule']['approvals_required']).to eq(approval_project_rule.approvals_required)
expect(rule['eligible_approvers']).to match([hash_including('id' => approver.id)])
expect(rule['users']).to match([hash_including('id' => approver.id)])
expect(rule['groups']).to match([hash_including('id' => group.id)])
end
end end
end end
end end
......
...@@ -195,14 +195,19 @@ RSpec.describe ApprovalRules::CreateService do ...@@ -195,14 +195,19 @@ RSpec.describe ApprovalRules::CreateService do
it_behaves_like "creatable" it_behaves_like "creatable"
context 'when project rule id is present' do context 'when project rule id is present' do
let_it_be(:project_user) { create(:user) }
let_it_be(:public_group) { create(:group, :public) }
let(:project_user_approvers) { [project_user] }
let(:group_user_approvers) { [public_group] }
let(:merge_request_approvers) { {} }
let(:project_rule) do let(:project_rule) do
create( create(
:approval_project_rule, :approval_project_rule,
project: project, project: project,
name: 'bar', name: 'bar',
approvals_required: 1, approvals_required: 1,
users: [create(:user)], users: project_user_approvers,
groups: [create(:group)] groups: group_user_approvers
) )
end end
...@@ -210,26 +215,70 @@ RSpec.describe ApprovalRules::CreateService do ...@@ -210,26 +215,70 @@ RSpec.describe ApprovalRules::CreateService do
described_class.new(target, user, { described_class.new(target, user, {
name: 'foo', name: 'foo',
approvals_required: 0, approvals_required: 0,
approval_project_rule_id: project_rule.id, approval_project_rule_id: project_rule.id
user_ids: [], }.merge(merge_request_approvers)).execute
group_ids: []
}).execute
end end
let(:rule) { result[:rule] } let(:rule) { result[:rule] }
it 'associates with project rule' do before do
project.add_developer(project_user)
end
it 'associates with project rule and copies its properites' do
expect(result[:status]).to eq(:success) expect(result[:status]).to eq(:success)
expect(rule.approvals_required).to eq(0) expect(rule.approvals_required).to eq(0)
expect(rule.approval_project_rule).to eq(project_rule) expect(rule.approval_project_rule).to eq(project_rule)
end
it 'copies properties from the project rule' do
expect(rule.name).to eq(project_rule.name) expect(rule.name).to eq(project_rule.name)
expect(rule.rule_type).to eq('regular')
expect(rule.users).to match(project_rule.users) expect(rule.users).to match(project_rule.users)
expect(rule.groups).to match(project_rule.groups) expect(rule.groups).to match(project_rule.groups)
end end
context 'when project rule includes no specific approvers' do
let(:project_user_approvers) { User.none }
let(:group_user_approvers) { Group.none }
it 'associates with project rule and copies its properties' do
expect(result[:status]).to eq(:success)
expect(rule.approvals_required).to eq(0)
expect(rule.approval_project_rule).to eq(project_rule)
expect(rule.name).to eq(project_rule.name)
expect(rule.rule_type).to eq('any_approver')
expect(rule.users).to match([])
expect(rule.groups).to match([])
end
end
context 'when merge request includes empty approvers' do
let(:merge_request_approvers) do
{
user_ids: [],
group_ids: []
}
end
it 'sets any approver' do
expect(result[:status]).to eq(:success)
expect(rule.name).to eq(project_rule.name)
expect(rule.rule_type).to eq('any_approver')
expect(rule.users).to eq([])
expect(rule.groups).to eq([])
end
end
context 'when merge request overrides approvers' do
let(:merge_request_approvers) { { user_ids: [user.id] } }
it 'sets single user as the approver' do
expect(result[:status]).to eq(:success)
expect(rule.name).to eq(project_rule.name)
expect(rule.rule_type).to eq('regular')
expect(rule.users).to eq([user])
expect(rule.groups).to eq([])
end
end
context 'when project rule is under the same project as MR' do context 'when project rule is under the same project as MR' do
let(:another_project) { create(:project) } let(:another_project) { create(:project) }
......
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