Commit 050e549a authored by Ash McKenzie's avatar Ash McKenzie

Merge branch '13712-public-mr-approval-state-api' into 'master'

Public MR-level approval state API endpoint

See merge request gitlab-org/gitlab-ee!15859
parents d9a49a5a d211a9cc
......@@ -525,6 +525,77 @@ PUT /projects/:id/merge_requests/:merge_request_iid/approvers
}
```
### Get the approval state of merge requests
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/13712) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.3.
You can request information about a merge request's approval state by using the following endpoint:
```
GET /projects/:id/merge_requests/:merge_request_iid/approval_state
```
The `approval_rules_overwritten` will be `true` if the merge request level rules
are created for the merge request. If there's none, it'll be `false`.
This includes additional information about the users who have already approved
(`approved_by`) and whether a rule is already approved (`approved`).
**Parameters:**
| Attribute | Type | Required | Description |
|----------------------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `merge_request_iid` | integer | yes | The IID of MR |
```json
{
"approval_rules_overwritten": true,
"rules": [
{
"id": 1,
"name": "Ruby",
"rule_type": "regular",
"eligible_approvers": [
{
"id": 4,
"name": "John Doe",
"username": "jdoe",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
"web_url": "http://localhost/jdoe"
}
],
"approvals_required": 2,
"users": [
{
"id": 4,
"name": "John Doe",
"username": "jdoe",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
"web_url": "http://localhost/jdoe"
}
],
"groups": [],
"contains_hidden_groups": false,
"approved_by": [
{
"id": 4,
"name": "John Doe",
"username": "jdoe",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
"web_url": "http://localhost/jdoe"
}
],
"source_rule": null,
"approved": true
}
]
}
```
### Get merge request level rules
>**Note:** This API endpoint is only available on 12.3 Starter and above.
......
---
title: Public MR-level approval state API endpoint
merge_request: 15859
author:
type: added
......@@ -26,6 +26,12 @@ module API
render_api_error!(errors, 400)
end
def present_merge_request_approval_state(presenter:)
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request.approval_state, with: presenter, current_user: current_user
end
end
params do
......@@ -57,9 +63,14 @@ module API
hidden: true
}
get 'approval_settings' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present_merge_request_approval_state(presenter: ::EE::API::Entities::MergeRequestApprovalSettings)
end
present merge_request.approval_state, with: ::EE::API::Entities::MergeRequestApprovalSettings, current_user: current_user
desc 'Get approval state of merge request' do
success ::EE::API::Entities::MergeRequestApprovalState
end
get 'approval_state' do
present_merge_request_approval_state(presenter: ::EE::API::Entities::MergeRequestApprovalState)
end
desc 'Change approval-related configuration' do
......
......@@ -326,14 +326,6 @@ module EE
expose :contains_hidden_groups?, as: :contains_hidden_groups
end
# Being used in private project-level approvals API.
# This overrides the `eligible_approvers` to be exposed as `approvers`.
#
# To be removed in https://gitlab.com/gitlab-org/gitlab-ee/issues/13574.
class ApprovalSettingRule < ApprovalRule
expose :approvers, using: ::API::Entities::UserBasic, override: true
end
class MergeRequestApprovalRule < ApprovalRule
class SourceRule < Grape::Entity
expose :approvals_required
......@@ -342,34 +334,52 @@ module EE
expose :source_rule, using: SourceRule
end
# Being used in private MR-level approvals API.
# This overrides the `eligible_approvers` to be exposed as `approvers` and
# include additional properties.
#
# To be made public in https://gitlab.com/gitlab-org/gitlab-ee/issues/13712
# and the `approvers` override can be removed.
class MergeRequestApprovalSettingRule < MergeRequestApprovalRule
expose :approvers, using: ::API::Entities::UserBasic, override: true
class MergeRequestApprovalStateRule < MergeRequestApprovalRule
expose :code_owner
expose :approved_approvers, as: :approved_by, using: ::API::Entities::UserBasic
expose :approved?, as: :approved
end
# Decorates ApprovalState
class MergeRequestApprovalSettings < Grape::Entity
class MergeRequestApprovalState < Grape::Entity
expose :approval_rules_overwritten do |approval_state|
approval_state.approval_rules_overwritten?
end
expose :wrapped_approval_rules, as: :rules, using: MergeRequestApprovalSettingRule
expose :wrapped_approval_rules, as: :rules, using: MergeRequestApprovalStateRule
end
# Being used in private project-level approvals API.
# This overrides the `eligible_approvers` to be exposed as `approvers`.
#
# To be removed in https://gitlab.com/gitlab-org/gitlab-ee/issues/13574.
class ApprovalSettingRule < ApprovalRule
expose :approvers, using: ::API::Entities::UserBasic, override: true
end
# Decorates Project
# Being used in private project-level approvals API.
#
# To be removed in https://gitlab.com/gitlab-org/gitlab-ee/issues/13574.
class ProjectApprovalSettings < Grape::Entity
expose :visible_approval_rules, as: :rules, using: ApprovalSettingRule
expose :min_fallback_approvals, as: :fallback_approvals_required
end
# Being used in private MR-level approvals API.
# This overrides the `eligible_approvers` to be exposed as `approvers`.
#
# To be removed in https://gitlab.com/gitlab-org/gitlab-ee/issues/13574.
class MergeRequestApprovalSettingRule < MergeRequestApprovalStateRule
expose :approvers, using: ::API::Entities::UserBasic, override: true
end
# Being used in private MR-level approvals API.
# This overrides the `rules` to be exposed using MergeRequestApprovalSettingRule.
#
# To be removed in https://gitlab.com/gitlab-org/gitlab-ee/issues/13574.
class MergeRequestApprovalSettings < MergeRequestApprovalState
expose :wrapped_approval_rules, as: :rules, using: MergeRequestApprovalSettingRule, override: true
end
# @deprecated
class Approver < Grape::Entity
expose :user, using: ::API::Entities::UserBasic
......
......@@ -10,6 +10,49 @@ describe API::MergeRequestApprovals do
set(:approver) { create :user }
set(:group) { create :group }
shared_examples_for 'an API endpoint for getting merge request approval state' do
context 'when source rule is present' do
let(:source_rule) { create(:approval_project_rule, project: project, approvals_required: 1, name: 'zoo') }
before do
rule.create_approval_merge_request_rule_source!(approval_project_rule: source_rule)
get api(url, user)
end
it 'returns source rule details' do
expect(json_response['rules'].first['source_rule']['approvals_required']).to eq(source_rule.approvals_required)
end
end
context 'when rule has groups' do
before do
rule.groups << group
get api(url, user)
end
context 'when user can view a group' do
let(:group) { create(:group) }
it 'includes group' do
rule = json_response['rules'].first
expect(rule['groups'].size).to eq(1)
expect(rule['groups']).to match([hash_including('id' => group.id)])
end
end
context 'when user cannot view a group included in groups' do
let(:group) { create(:group, :private) }
it 'excludes private groups' do
expect(json_response['rules'].first['groups'].size).to eq(0)
end
end
end
end
describe 'GET :id/merge_requests/:merge_request_iid/approvals' do
let!(:rule) { create(:approval_merge_request_rule, merge_request: merge_request, approvals_required: 2, name: 'foo') }
......@@ -96,14 +139,19 @@ describe API::MergeRequestApprovals do
end
describe 'GET :id/merge_requests/:merge_request_iid/approval_settings' do
let!(:rule) { create(:approval_merge_request_rule, merge_request: merge_request, approvals_required: 2, name: 'foo') }
let(:rule) { create(:approval_merge_request_rule, merge_request: merge_request, approvals_required: 2, name: 'foo') }
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_settings" }
it 'retrieves the approval rules details' do
before do
project.add_developer(approver)
merge_request.approvals.create(user: approver)
rule.users << approver
end
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_settings", user)
it_behaves_like 'an API endpoint for getting merge request approval state'
it 'retrieves the approval rules details' do
get api(url, user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['rules'].size).to eq(1)
......@@ -116,35 +164,22 @@ describe API::MergeRequestApprovals do
expect(rule_response['approved_by'][0]['username']).to eq(approver.username)
expect(rule_response['source_rule']).to eq(nil)
end
end
context 'when source rule is present' do
let!(:source_rule) { create(:approval_project_rule, project: project, approvals_required: 1, name: 'zoo') }
let!(:rule) { create(:approval_merge_request_rule, merge_request: merge_request, approvals_required: 2, name: 'foo') }
it 'returns source rule details' do
project.add_developer(approver)
merge_request.approvals.create(user: approver)
rule.users << approver
rule.create_approval_merge_request_rule_source!(approval_project_rule: source_rule)
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_settings", user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['rules'].size).to eq(1)
rule_response = json_response['rules'].first
describe 'GET :id/merge_requests/:merge_request_iid/approval_state' do
let(:rule) { create(:approval_merge_request_rule, merge_request: merge_request, approvals_required: 2, name: 'foo') }
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_state" }
expect(rule_response['source_rule']['approvals_required']).to eq(source_rule.approvals_required)
end
before do
project.add_developer(approver)
merge_request.approvals.create(user: approver)
rule.users << approver
end
it 'excludes private groups' do
private_group = create(:group, :private)
rule.users << approver
rule.groups << private_group
it_behaves_like 'an API endpoint for getting merge request approval state'
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_settings", user)
it 'retrieves the approval state details' do
get api(url, user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['rules'].size).to eq(1)
......@@ -152,7 +187,10 @@ describe API::MergeRequestApprovals do
rule_response = json_response['rules'].first
expect(rule_response['id']).to eq(rule.id)
expect(rule_response['groups'].size).to eq(0)
expect(rule_response['name']).to eq('foo')
expect(rule_response['eligible_approvers'][0]['username']).to eq(approver.username)
expect(rule_response['approved_by'][0]['username']).to eq(approver.username)
expect(rule_response['source_rule']).to eq(nil)
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