Skip to content

Commit 10f3759

Browse files
djachkovgonchik
authored andcommitted
Bitbucket: add branch-permissions methods, add some docs, add examples (#400)
1 parent 50ef27e commit 10f3759

File tree

4 files changed

+336
-8
lines changed

4 files changed

+336
-8
lines changed

atlassian/bitbucket.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
class Bitbucket(AtlassianRestAPI):
1010

11+
bulk_headers = {"Content-Type": "application/vnd.atl.bitbucket.bulk+json"}
12+
1113
def project_list(self, limit=None):
1214
"""
1315
Provide the project list
@@ -783,6 +785,89 @@ def create_pull_request(self, project_key, repository, data):
783785
repository=repository)
784786
return self.post(url, data=data)
785787

788+
def decline_pull_request(self, project_key, repository, pr_id, pr_version):
789+
"""
790+
Decline a pull request.
791+
The authenticated user must have REPO_READ permission for the repository
792+
that this pull request targets to call this resource.
793+
794+
:param project_key: PROJECT
795+
:param repository: my_shiny_repo
796+
:param pr_id: 2341
797+
:param pr_version: 12
798+
:return:
799+
"""
800+
url = 'rest/api/1.0/projects/{project_key}/repos/{repository}/pull-requests/{pr_id}/decline'.format(
801+
project_key=project_key,
802+
repository=repository,
803+
pr_id=pr_id
804+
)
805+
params = {'version': pr_version}
806+
807+
return self.post(url, params=params)
808+
809+
def is_pull_request_can_be_merged(self, project_key, repository, pr_id):
810+
"""
811+
Test whether a pull request can be merged.
812+
A pull request may not be merged if:
813+
- there are conflicts that need to be manually resolved before merging; and/or
814+
- one or more merge checks have vetoed the merge.
815+
The authenticated user must have REPO_READ permission for the repository
816+
that this pull request targets to call this resource.
817+
818+
:param project_key: PROJECT
819+
:param repository: my_shiny_repo
820+
:param pr_id: 2341
821+
:return:
822+
"""
823+
url = 'rest/api/1.0/projects/{project_key}/repos/{repository}/pull-requests/{pr_id}/merge'.format(
824+
project_key=project_key,
825+
repository=repository,
826+
pr_id=pr_id
827+
)
828+
return self.get(url)
829+
830+
def merge_pull_request(self, project_key, repository, pr_id, pr_version):
831+
"""
832+
Merge pull request
833+
The authenticated user must have REPO_READ permission for the repository
834+
that this pull request targets to call this resource.
835+
836+
:param project_key: PROJECT
837+
:param repository: my_shiny_repo
838+
:param pr_id: 2341
839+
:return:
840+
"""
841+
url = 'rest/api/1.0/projects/{project_key}/repos/{repository}/pull-requests/{pr_id}/merge'.format(
842+
project_key=project_key,
843+
repository=repository,
844+
pr_id=pr_id
845+
)
846+
params = {'version': pr_version}
847+
848+
return self.post(url, params=params)
849+
850+
def reopen_pull_request(self, project_key, repository, pr_id, pr_version):
851+
"""
852+
Re-open a declined pull request.
853+
The authenticated user must have REPO_READ permission for the repository
854+
that this pull request targets to call this resource.
855+
856+
:param project_key: PROJECT
857+
:param repository: my_shiny_repo
858+
:param pr_id: 2341
859+
:param pr_version: 12
860+
:return:
861+
"""
862+
url = 'rest/api/1.0/projects/{project_key}/repos/{repository}/pull-requests/{pr_id}/reopen'.format(
863+
project_key=project_key,
864+
repository=repository,
865+
pr_id=pr_id
866+
)
867+
params = {'version': pr_version}
868+
869+
return self.post(url, params=params)
870+
786871
def check_inbox_pull_requests_count(self):
787872
return self.get('rest/api/1.0/inbox/pull-requests/count')
788873

@@ -1135,6 +1220,109 @@ def get_branches_permissions(self, project, repository=None, start=0, limit=25):
11351220
params['start'] = start
11361221
return self.get(url, params=params)
11371222

1223+
def set_branches_permissions(
1224+
self, project_key,
1225+
multiple_permissions=False,
1226+
matcher_type=None,
1227+
matcher_value=None,
1228+
permission_type=None,
1229+
repository=None,
1230+
except_users=[],
1231+
except_groups=[],
1232+
except_access_keys=[],
1233+
start=0,
1234+
limit=25
1235+
):
1236+
"""
1237+
Create a restriction for the supplied branch or set of branches to be applied to the given repository.
1238+
Allows creating multiple restrictions at once.
1239+
To use multiple restrictions you should format payload manually - see the bitbucket-branch-restrictions.py example.
1240+
Reference: https://docs.atlassian.com/bitbucket-server/rest/6.8.0/bitbucket-ref-restriction-rest.html
1241+
1242+
:param project_key:
1243+
:param repository:
1244+
:return:
1245+
"""
1246+
headers = self.default_headers
1247+
if repository:
1248+
url = "/rest/branch-permissions/2.0/projects/{project_key}/repos/{repository}/restrictions".format(
1249+
project_key=project_key,
1250+
repository=repository
1251+
)
1252+
else:
1253+
url = "/rest/branch-permissions/2.0/projects/{project_key}/restrictions".format(
1254+
project_key=project_key
1255+
)
1256+
if multiple_permissions:
1257+
headers = self.bulk_headers
1258+
restriction = multiple_permissions
1259+
else:
1260+
restriction = {
1261+
"type": permission_type,
1262+
"matcher": {
1263+
"id": matcher_value,
1264+
"displayId": matcher_value,
1265+
"type": {
1266+
"id": matcher_type.upper(),
1267+
"name": matcher_type.capitalize()
1268+
},
1269+
"active": True,
1270+
},
1271+
"users": except_users,
1272+
"groups": except_groups,
1273+
"accessKeys": except_access_keys,
1274+
}
1275+
params = {"start": start, "limit": limit}
1276+
return self.post(url, data=restriction, params=params, headers=headers)
1277+
1278+
def delete_branch_permission(self, project_key, permission_id, repository=None):
1279+
"""
1280+
Deletes a restriction as specified by a restriction id.
1281+
The authenticated user must have REPO_ADMIN permission or higher to call this resource.
1282+
1283+
:param project_key:
1284+
:param repository:
1285+
:param permission_id:
1286+
:return:
1287+
"""
1288+
1289+
if repository:
1290+
url = "/rest/branch-permissions/2.0/projects/{project_key}/repos/{repository}/restrictions/{id}".format(
1291+
project_key=project_key,
1292+
repository=repository,
1293+
id=permission_id
1294+
)
1295+
else:
1296+
url = "/rest/branch-permissions/2.0/projects/{project_key}/restrictions/{id}".format(
1297+
project_key=project_key,
1298+
id=permission_id
1299+
)
1300+
return self.delete(url)
1301+
1302+
def get_branch_permission(self, project_key, permission_id, repository=None):
1303+
"""
1304+
Returns a restriction as specified by a restriction id.
1305+
The authenticated user must have REPO_ADMIN permission or higher to call this resource.
1306+
1307+
:param project_key:
1308+
:param repository:
1309+
:param permission_id:
1310+
:return:
1311+
"""
1312+
1313+
if repository:
1314+
url = "/rest/branch-permissions/2.0/projects/{project_key}/repos/{repository}/restrictions/{id}".format(
1315+
project_key=project_key,
1316+
repository=repository,
1317+
id=permission_id
1318+
)
1319+
else:
1320+
url = "/rest/branch-permissions/2.0/projects/{project_key}/restrictions/{id}".format(
1321+
project_key=project_key,
1322+
id=permission_id
1323+
)
1324+
return self.get(url)
1325+
11381326
def all_branches_permissions(self, project, repository=None):
11391327
"""
11401328
Get branches permissions from a given repo

docs/bitbucket.rst

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ Manage repositories
7676
'prefix': 'release/'}]}
7777
bitbucket.set_branching_model(project_key, repo_key, data)
7878
79-
bitbucket.repo_users(self, project_key, repo_key, limit=99999, filter_str=None)
79+
bitbucket.repo_users(project_key, repo_key, limit=99999, filter_str=None)
8080
81-
bitbucket.repo_groups(self, project_key, repo_key, limit=99999, filter_str=None)
81+
bitbucket.repo_groups(project_key, repo_key, limit=99999, filter_str=None)
8282
8383
# Grant repository permission to an specific user
8484
bitbucket.repo_grant_user_permissions(project_key, repo_key, username, permission)
@@ -112,7 +112,7 @@ Manage code
112112
# Requires an existing project in which this repository will be created. The only parameters which will be used
113113
# are name and scmId.
114114
# The authenticated user must have PROJECT_ADMIN permission for the context project to call this resource.
115-
bitbucket.create_repo(project_key, repository, forkable=False, is_private=True):
115+
bitbucket.create_repo(project_key, repository, forkable=False, is_private=True)
116116
117117
# Get branches from repo
118118
bitbucket.get_branches(project, repository, filter='', limit=99999, details=True)
@@ -128,16 +128,16 @@ Manage code
128128
bitbucket.get_pull_requests(project, repository, state='OPEN', order='newest', limit=100, start=0)
129129
130130
# Get pull request activities
131-
bitbucket.get_pull_requests_activities(self, project, repository, pull_request_id)
131+
bitbucket.get_pull_requests_activities(project, repository, pull_request_id)
132132
133133
# Get pull request changes
134-
bitbucket.get_pull_requests_changes(self, project, repository, pull_request_id)
134+
bitbucket.get_pull_requests_changes(project, repository, pull_request_id)
135135
136136
# Get pull request commits
137-
bitbucket.get_pull_requests_commits(self, project, repository, pull_request_id)
137+
bitbucket.get_pull_requests_commits(project, repository, pull_request_id)
138138
139139
# Add comment into pull request
140-
bitbucket.add_pull_request_comment(self, project, repository, pull_request_id, text)
140+
bitbucket.add_pull_request_comment(project, repository, pull_request_id, text)
141141
142142
# Get tags for related repo
143143
bitbucket.get_tags(project, repository, filter='', limit=99999)
@@ -168,4 +168,35 @@ Manage code
168168
"""
169169
Retrieve the raw content for a file path at a specified revision.
170170
The authenticated user must have REPO_READ permission for the specified repository to call this resource.
171-
"""
171+
"""
172+
173+
Branch permissions
174+
------------------
175+
176+
.. code-block:: python
177+
178+
# Set branches permissions
179+
bitbucket.set_branches_permissions(project_key, multiple_permissions=False, matcher_type=None, matcher_value=None, permission_type=None, repository=None, except_users=[], except_groups=[], except_access_keys=[], start=0, limit=25)
180+
181+
# Delete a single branch permission by premission id
182+
bitbucket.delete_branch_permission(project_key, permission_id, repository=None)
183+
184+
# Get a single branch permission by permission id
185+
bitbucket.get_branch_permission(project_key, permission_id, repository=None)
186+
187+
Pull Request management
188+
-----------------------
189+
190+
.. code-block:: python
191+
192+
# Decline pull request
193+
bitbucket.decline_pull_request(project_key, repository, pr_id, pr_version)
194+
195+
# Check if pull request can be merged
196+
bitbucket.is_pull_request_can_be_merged(project_key, repository, pr_id)
197+
198+
# Merge pull request
199+
bitbucket.merge_pull_request(project_key, repository, pr_id, pr_version)
200+
201+
# Reopen pull request
202+
bitbucket.reopen_pull_request(project_key, repository, pr_id, pr_version)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# coding=utf-8
2+
from atlassian import Bitbucket
3+
4+
"""
5+
For all possible arguments and values please visit:
6+
https://docs.atlassian.com/bitbucket-server/rest/latest/bitbucket-ref-restriction-rest.html
7+
"""
8+
bitbucket = Bitbucket(
9+
url='http://localhost:7990',
10+
username='admin',
11+
password='admin',
12+
advanced_mode=True) # For more simple response handling
13+
14+
single_permission = bitbucket.set_branches_permissions(
15+
"PROJECT_KEY",
16+
matcher_type="branch", # lowercase matcher type
17+
matcher_value="master",
18+
permission_type="no-deletes",
19+
repository="repository_name",
20+
except_users=["user1", "user2"]
21+
)
22+
print(single_permission)
23+
pid = single_permission.json().get("id")
24+
25+
single_permission = bitbucket.get_branch_permission("PROJECT_KEY", pid)
26+
print(single_permission)
27+
28+
deleted_permission = bitbucket.delete_branch_permission("PROJECT_KEY", pid)
29+
print(deleted_permission)
30+
31+
multiplpe_permissions_payload = [
32+
{
33+
"type": "read-only",
34+
"matcher": {
35+
"id": "master",
36+
"displayId": "master",
37+
"type": {
38+
"id": "BRANCH",
39+
"name": "Branch"
40+
},
41+
"active": True
42+
},
43+
"users": [
44+
"user1",
45+
],
46+
"groups": [
47+
48+
],
49+
"accessKeys": []
50+
},
51+
{
52+
"type": "pull-request-only",
53+
"matcher": {
54+
"id": "refs/tags/**",
55+
"displayId": "refs/tags/**",
56+
"type": {
57+
"id": "PATTERN",
58+
"name": "Pattern"
59+
},
60+
"active": True
61+
},
62+
"users": [
63+
"user2"
64+
],
65+
"groups": [
66+
67+
],
68+
"accessKeys": []
69+
}
70+
]
71+
multiple_permissions = bitbucket.set_branches_permissions(
72+
"PROJECT_KEY",
73+
multiple_permissions=multiplpe_permissions_payload,
74+
matcher_type="branch",
75+
matcher_value="master",
76+
permission_type="no-deletes",
77+
repository="repository_name",
78+
users=["user1", "user2"]
79+
)
80+
print(multiple_permissions)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# coding=utf-8
2+
from atlassian import Bitbucket
3+
4+
bitbucket = Bitbucket(
5+
url='http://localhost:7990',
6+
username='admin',
7+
password='admin')
8+
pr_id = 12345
9+
10+
pr = bitbucket.get_pullrequest("project_name", "repository_name", pr_id)
11+
ver = pr.json().get("version")
12+
print(f"PR version: {ver}")
13+
14+
response = bitbucket.decline_pull_request("project_name", "repository_name", pr_id, ver)
15+
print(f"Declined: {response}")
16+
ver = response.json().get("version")
17+
print(f"PR version: {ver}")
18+
19+
response = bitbucket.reopen_pull_request("project_name", "repository_name", pr_id, ver)
20+
print(f"Reopen: {response}")
21+
ver = response.json().get("version")
22+
print(f"PR version: {ver}")
23+
24+
response = bitbucket.is_pull_request_can_be_merged("project_name", "repository_name", pr_id)
25+
print(f"Reopen: {response}")
26+
print(f"PR version: {ver}")
27+
28+
response = bitbucket.merge_pull_request("project_name", "repository_name", pr_id, ver)
29+
print(f"Merged: {response}")

0 commit comments

Comments
 (0)