Commit a3d5ef01 authored by Patrick Bajao's avatar Patrick Bajao

API endpoint for listing MR-level approval rules

This is just like the project-level rules API equivalent.
Difference is that this include a `source_rule` property.
parent 05a5a55e
# frozen_string_literal: true
module API
module Helpers
module ApprovalHelpers
def present_approval(merge_request)
present merge_request.approval_state, with: ::EE::API::Entities::ApprovalState, current_user: current_user
end
end
end
end
# frozen_string_literal: true
module API
class MergeRequestApprovalRules < ::Grape::API
params do
requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
end
resource :projects, requirements: ::API::API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
segment ':id/merge_requests/:merge_request_iid/approval_rules' do
desc 'Get all merge request approval rules' do
success EE::API::Entities::MergeRequestApprovalRule
end
get do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request.approval_rules, with: EE::API::Entities::MergeRequestApprovalRule, current_user: current_user
end
end
end
end
end
......@@ -6,8 +6,11 @@ module API
ARRAY_COERCION_LAMBDA = ->(val) { val.empty? ? [] : Array.wrap(val) }
helpers ::API::Helpers::ApprovalHelpers
helpers do
def present_approval(merge_request)
present merge_request.approval_state, with: ::EE::API::Entities::ApprovalState, current_user: current_user
end
def handle_merge_request_errors!(errors)
if errors.has_key? :project_access
error!(errors[:project_access], 422)
......@@ -50,13 +53,13 @@ module API
end
desc 'List approval rules for merge request', {
success: ::EE::API::Entities::MergeRequestApprovalRules,
success: ::EE::API::Entities::MergeRequestApprovalSettings,
hidden: true
}
get 'approval_settings' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request.approval_state, with: ::EE::API::Entities::MergeRequestApprovalRules, current_user: current_user
present merge_request.approval_state, with: ::EE::API::Entities::MergeRequestApprovalSettings, current_user: current_user
end
desc 'Change approval-related configuration' do
......
......@@ -34,6 +34,7 @@ module EE
mount ::API::ProjectApprovals
mount ::API::Vulnerabilities
mount ::API::MergeRequestApprovals
mount ::API::MergeRequestApprovalRules
mount ::API::ProjectAliases
mount ::API::Dependencies
......
......@@ -317,41 +317,45 @@ module EE
expose :id, :name, :rule_type
end
class ApprovalSettingRule < ApprovalRuleShort
class ApprovalRule < ApprovalRuleShort
def initialize(object, options = {})
presenter = ::ApprovalRulePresenter.new(object, current_user: options[:current_user])
super(presenter, options)
end
expose :approvers, using: ::API::Entities::UserBasic
expose :approvers, as: :eligible_approvers, using: ::API::Entities::UserBasic
expose :approvals_required
expose :users, using: ::API::Entities::UserBasic
expose :groups, using: ::API::Entities::Group
expose :contains_hidden_groups?, as: :contains_hidden_groups
end
class ApprovalRule < ApprovalSettingRule
expose :approvers, as: :eligible_approvers, using: ::API::Entities::UserBasic, override: true
class ApprovalSettingRule < ApprovalRule
expose :approvers, using: ::API::Entities::UserBasic, override: true
end
class MergeRequestApprovalRule < ApprovalSettingRule
class MergeRequestApprovalRule < ApprovalRule
class SourceRule < Grape::Entity
expose :approvals_required
end
expose :approved_approvers, as: :approved_by, using: ::API::Entities::UserBasic
expose :code_owner
expose :source_rule, using: SourceRule
end
class MergeRequestApprovalSettingRule < MergeRequestApprovalRule
expose :approvers, using: ::API::Entities::UserBasic, override: true
expose :code_owner
expose :approved_approvers, as: :approved_by, using: ::API::Entities::UserBasic
expose :approved?, as: :approved
end
# Decorates ApprovalState
class MergeRequestApprovalRules < Grape::Entity
class MergeRequestApprovalSettings < Grape::Entity
expose :approval_rules_overwritten do |approval_state|
approval_state.approval_rules_overwritten?
end
expose :wrapped_approval_rules, as: :rules, using: MergeRequestApprovalRule
expose :wrapped_approval_rules, as: :rules, using: MergeRequestApprovalSettingRule
end
# Decorates Project
......
{
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"approvals_required": { "type": "integer" },
"contains_hidden_groups": { "type": "boolean" },
"rule_type": { "type": "string" },
"source_rule": {
"type":["null", "object"],
"properties": {}
},
"eligible_approvers": {
"type": "array",
"items": {
"type": "object",
"properties": {}
}
},
"groups": {
"type": "array",
"items": {
"type": "object",
"properties": {}
}
},
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {}
}
}
},
"additionalProperties": false
}
{
"type": "array",
"items": { "$ref": "./merge_request_approval_rule.json" }
}
# frozen_string_literal: true
require 'spec_helper'
describe API::MergeRequestApprovalRules do
set(:user) { create(:user) }
set(:other_user) { create(:user) }
set(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace) }
describe 'GET /projects/:id/merge_requests/:merge_request_iid/approval_rules' do
let(:merge_request) { create(:merge_request, author: user, source_project: project, target_project: project) }
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_rules" }
context 'user cannot read merge request' do
before do
project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
get api(url, other_user)
end
it 'responds with 403' do
expect(response).to have_gitlab_http_status(403)
end
end
context 'use can read merge request' do
let(:current_user) { other_user }
let(:approver) { create(:user) }
let(:group) { create(:group) }
let(:source_rule) { nil }
let(:users) { [approver] }
let(:groups) { [group] }
let!(:mr_approval_rule) do
create(
:approval_merge_request_rule,
merge_request: merge_request,
approval_project_rule: source_rule,
users: users,
groups: groups
)
end
before do
group.add_developer(approver)
merge_request.approvals.create(user: approver)
get api(url, current_user)
end
it 'matches the response schema' do
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/merge_request_approval_rules', dir: 'ee')
rules = json_response
expect(rules.size).to eq(1)
expect(rules.first['name']).to eq(mr_approval_rule.name)
expect(rules.first['approvals_required']).to eq(mr_approval_rule.approvals_required)
expect(rules.first['contains_hidden_groups']).to eq(false)
expect(rules.first['rule_type']).to eq(mr_approval_rule.rule_type)
expect(rules.first['source_rule']).to be_nil
expect(rules.first['eligible_approvers'].first['id']).to eq(approver.id)
expect(rules.first['users'].first['id']).to eq(approver.id)
expect(rules.first['groups'].first['id']).to eq(group.id)
end
context 'groups contain private groups' do
let(:group) { create(:group, :private) }
context 'current_user cannot see private group' do
it 'hides private group' do
rules = json_response
expect(rules.first['contains_hidden_groups']).to eq(true)
expect(rules.first['groups']).to be_empty
end
end
context 'current_user can see private group' do
let(:current_user) { approver }
it 'shows private group' do
rules = json_response
expect(rules.first['contains_hidden_groups']).to eq(false)
expect(rules.first['groups'].first['id']).to eq(group.id)
end
end
end
context 'has existing merge request rule that overrides a project-level rule' do
let(:source_rule) { create(:approval_project_rule, project: project) }
it 'includes source_rule' do
expect(json_response.first['source_rule']['approvals_required']).to eq(source_rule.approvals_required)
end
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