Skip to content

Feature/project merge request approval rules #1200

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions docs/gl_objects/mr_approvals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ References
+ :class:`gitlab.v4.objects.ProjectMergeRequestApproval`
+ :class:`gitlab.v4.objects.ProjectMergeRequestApprovalManager`
+ :attr:`gitlab.v4.objects.ProjectMergeRequest.approvals`
+ :class:`gitlab.v4.objects.ProjectMergeRequestApprovalRule`
+ :class:`gitlab.v4.objects.ProjectMergeRequestApprovalRuleManager`
+ :attr:`gitlab.v4.objects.ProjectMergeRequest.approval_rules`

* GitLab API: https://docs.gitlab.com/ee/api/merge_request_approvals.html

Expand Down Expand Up @@ -50,10 +53,33 @@ Change project-level or MR-level MR approvals settings::

mr_mras.set_approvers(approvals_required = 1)

Change project-level or MR-level MR allowed approvers::
Change project-level MR allowed approvers::

project.approvals.set_approvers(approver_ids=[105],
approver_group_ids=[653, 654])
approver_group_ids=[653, 654])

Create a new MR-level approval rule or change an existing MR-level approval rule::

mr.approvals.set_approvers(approvals_required = 1, approver_ids=[105],
approver_group_ids=[653, 654])
approver_group_ids=[653, 654],
approval_rule_name="my MR custom approval rule")

List MR-level MR approval rules::

mr.approval_rules.list()

Change MR-level MR approval rule::

mr_approvalrule.user_ids = [105]
mr_approvalrule.approvals_required = 2
mr_approvalrule.group_ids = [653, 654]
mr_approvalrule.save()

Create a MR-level MR approval rule::
mr.approval_rules.create({
"name": "my MR custom approval rule",
"approvals_required": 2,
"rule_type": "regular",
"user_ids": [105],
"group_ids": [653, 654],
})
294 changes: 294 additions & 0 deletions gitlab/tests/objects/test_project_merge_request_approvals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
"""
Gitlab API: https://docs.gitlab.com/ee/api/merge_request_approvals.html
"""

import pytest
import responses
import copy


approval_rule_id = 1
approval_rule_name = "security"
approvals_required = 3
user_ids = [5, 50]
group_ids = [5]

new_approval_rule_name = "new approval rule"
new_approval_rule_user_ids = user_ids
new_approval_rule_approvals_required = 2

updated_approval_rule_user_ids = [5]
updated_approval_rule_approvals_required = 1


@pytest.fixture
def resp_snippet():
merge_request_content = [
{
"id": 1,
"iid": 1,
"project_id": 1,
"title": "test1",
"description": "fixed login page css paddings",
"state": "merged",
"merged_by": {
"id": 87854,
"name": "Douwe Maan",
"username": "DouweM",
"state": "active",
"avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
"web_url": "https://gitlab.com/DouweM",
},
"merged_at": "2018-09-07T11:16:17.520Z",
"closed_by": None,
"closed_at": None,
"created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master",
"source_branch": "test1",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"name": "Administrator",
"username": "admin",
"state": "active",
"avatar_url": None,
"web_url": "https://gitlab.example.com/admin",
},
"assignee": {
"id": 1,
"name": "Administrator",
"username": "admin",
"state": "active",
"avatar_url": None,
"web_url": "https://gitlab.example.com/admin",
},
"assignees": [
{
"name": "Miss Monserrate Beier",
"username": "axel.block",
"id": 12,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
"web_url": "https://gitlab.example.com/axel.block",
}
],
"source_project_id": 2,
"target_project_id": 3,
"labels": ["Community contribution", "Manage"],
"work_in_progress": None,
"milestone": {
"id": 5,
"iid": 1,
"project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": "2018-09-22",
"start_date": "2018-08-08",
"web_url": "https://gitlab.example.com/my-group/my-project/milestones/1",
},
"merge_when_pipeline_succeeds": None,
"merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": None,
"squash_commit_sha": None,
"user_notes_count": 1,
"discussion_locked": None,
"should_remove_source_branch": True,
"force_remove_source_branch": False,
"allow_collaboration": False,
"allow_maintainer_to_push": False,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "my-group/my-project!1",
"full": "my-group/my-project!1",
},
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": None,
"human_total_time_spent": None,
},
"squash": False,
"task_completion_status": {"count": 0, "completed_count": 0},
}
]
mr_ars_content = [
{
"id": approval_rule_id,
"name": approval_rule_name,
"rule_type": "regular",
"eligible_approvers": [
{
"id": user_ids[0],
"name": "John Doe",
"username": "jdoe",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
"web_url": "http://localhost/jdoe",
},
{
"id": user_ids[1],
"name": "Group Member 1",
"username": "group_member_1",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
"web_url": "http://localhost/group_member_1",
},
],
"approvals_required": approvals_required,
"source_rule": None,
"users": [
{
"id": 5,
"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": [
{
"id": 5,
"name": "group1",
"path": "group1",
"description": "",
"visibility": "public",
"lfs_enabled": False,
"avatar_url": None,
"web_url": "http://localhost/groups/group1",
"request_access_enabled": False,
"full_name": "group1",
"full_path": "group1",
"parent_id": None,
"ldap_cn": None,
"ldap_access": None,
}
],
"contains_hidden_groups": False,
"overridden": False,
}
]

with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/projects/1/merge_requests",
json=merge_request_content,
content_type="application/json",
status=200,
)
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/projects/1/merge_requests/1",
json=merge_request_content[0],
content_type="application/json",
status=200,
)
rsps.add(
method=responses.GET,
url="http://localhost/api/v4/projects/1/merge_requests/1/approval_rules",
json=mr_ars_content,
content_type="application/json",
status=200,
)

new_mr_ars_content = dict(mr_ars_content[0])
new_mr_ars_content["name"] = new_approval_rule_name
new_mr_ars_content["approvals_required"] = new_approval_rule_approvals_required

rsps.add(
method=responses.POST,
url="http://localhost/api/v4/projects/1/merge_requests/1/approval_rules",
json=new_mr_ars_content,
content_type="application/json",
status=200,
)

updated_mr_ars_content = copy.deepcopy(mr_ars_content[0])
updated_mr_ars_content["eligible_approvers"] = [
mr_ars_content[0]["eligible_approvers"][0]
]

updated_mr_ars_content[
"approvals_required"
] = updated_approval_rule_approvals_required

rsps.add(
method=responses.PUT,
url="http://localhost/api/v4/projects/1/merge_requests/1/approval_rules/1",
json=updated_mr_ars_content,
content_type="application/json",
status=200,
)
yield rsps


def test_list_merge_request_approval_rules(project, resp_snippet):
approval_rules = project.mergerequests.get(1).approval_rules.list()
assert len(approval_rules) == 1
assert approval_rules[0].name == approval_rule_name
assert approval_rules[0].id == approval_rule_id


def test_update_merge_request_approvals_set_approvers(project, resp_snippet):
approvals = project.mergerequests.get(1).approvals
response = approvals.set_approvers(
updated_approval_rule_approvals_required,
approver_ids=updated_approval_rule_user_ids,
approver_group_ids=group_ids,
approval_rule_name=approval_rule_name,
)

assert response.approvals_required == updated_approval_rule_approvals_required
assert len(response.eligible_approvers) == len(updated_approval_rule_user_ids)
assert response.eligible_approvers[0]["id"] == updated_approval_rule_user_ids[0]
assert response.name == approval_rule_name


def test_create_merge_request_approvals_set_approvers(project, resp_snippet):
approvals = project.mergerequests.get(1).approvals
response = approvals.set_approvers(
new_approval_rule_approvals_required,
approver_ids=new_approval_rule_user_ids,
approver_group_ids=group_ids,
approval_rule_name=new_approval_rule_name,
)
assert response.approvals_required == new_approval_rule_approvals_required
assert len(response.eligible_approvers) == len(new_approval_rule_user_ids)
assert response.eligible_approvers[0]["id"] == new_approval_rule_user_ids[0]
assert response.name == new_approval_rule_name


def test_create_merge_request_approval_rule(project, resp_snippet):
approval_rules = project.mergerequests.get(1).approval_rules
data = {
"name": new_approval_rule_name,
"approvals_required": new_approval_rule_approvals_required,
"rule_type": "regular",
"user_ids": new_approval_rule_user_ids,
"group_ids": group_ids,
}
response = approval_rules.create(data)
assert response.approvals_required == new_approval_rule_approvals_required
assert len(response.eligible_approvers) == len(new_approval_rule_user_ids)
assert response.eligible_approvers[0]["id"] == new_approval_rule_user_ids[0]
assert response.name == new_approval_rule_name


def test_update_merge_request_approval_rule(project, resp_snippet):
approval_rules = project.mergerequests.get(1).approval_rules
ar_1 = approval_rules.list()[0]
ar_1.user_ids = updated_approval_rule_user_ids
ar_1.approvals_required = updated_approval_rule_approvals_required
ar_1.save()

assert ar_1.approvals_required == updated_approval_rule_approvals_required
assert len(ar_1.eligible_approvers) == len(updated_approval_rule_user_ids)
assert ar_1.eligible_approvers[0]["id"] == updated_approval_rule_user_ids[0]
Loading