Skip to content

Commit 49858f3

Browse files
authored
[Bitbucket Cloud] Add basic pull request support (#655)
* Add basic pull request support for Bitbucket Cloud * fixes for py2.7 support * remove typing import * - Rename DefaultReviewer to User - PullRequest -> state replaced by is_declined, is_merged, is_open, is_superseded - Participant -> role replaced by is_participant, is_reviewer * Participant -> state replaced by has_approved/has_changes_requested * Add comment, merge, approve, unapporve to PullRequest * - Add raw_timeformat parameter - PullRequest decline - User placed in common - DefaultReviewer inherits from User - Use get_time() Method for date values * - Fix decline API endpoint cant receive a reason - Fix raw_timeformat in Bitbucket() * use get_time() * add const.py * const correction * add timeformat to const * - Replaced raw_timeformat with timeformat_func - Possibility to specify a lamda function for time convert * fix flake8 * - Remove const.py - timeformat_lambda centralized - Remove duplicate approved() method in participant * fix DefaultReviewer + test * fix black * add (un)request_changes , fix test * - API doc links - timeformat_lambda behavior changed as discussed in #655 * - use default merge strategy as default on pr merge - CONF_TIMEFORMAT as class CONSTANT * - remove default_merge_strategy - fix decline() * add unit tests * fix py27
1 parent bc15649 commit 49858f3

File tree

20 files changed

+1349
-34
lines changed

20 files changed

+1349
-34
lines changed

atlassian/bitbucket/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def __init__(self, url, *args, **kwargs):
1717
kwargs["api_version"] = "2.0" if "cloud" in kwargs and kwargs["cloud"] else "1.0"
1818
if "cloud" in kwargs:
1919
kwargs["api_root"] = "" if "api.bitbucket.org" in url else "rest/api"
20+
2021
super(Bitbucket, self).__init__(url, *args, **kwargs)
2122

2223
def markup_preview(self, data):

atlassian/bitbucket/base.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# coding=utf-8
22

3+
from datetime import datetime
34
from ..rest_client import AtlassianRestAPI
45

56

67
class BitbucketBase(AtlassianRestAPI):
8+
CONF_TIMEFORMAT = "%Y-%m-%dT%H:%M:%S.%f%z"
79
bulk_headers = {"Content-Type": "application/vnd.atl.bitbucket.bulk+json"}
810

911
def __init__(self, url, *args, **kwargs):
@@ -15,6 +17,8 @@ def __init__(self, url, *args, **kwargs):
1517
1618
:return: nothing
1719
"""
20+
self.timeformat_lambda = kwargs.pop("timeformat_lambda", lambda x: self._default_timeformat_lambda(x))
21+
self._check_timeformat_lambda()
1822
super(BitbucketBase, self).__init__(url, *args, **kwargs)
1923

2024
def _get_paged(self, url, params=None, data=None, flags=None, trailing=None, absolute=False):
@@ -54,11 +58,37 @@ def _get_paged(self, url, params=None, data=None, flags=None, trailing=None, abs
5458

5559
return
5660

61+
def _check_timeformat_lambda(self):
62+
LAMBDA = lambda: 0 # noqa: E731
63+
if self.timeformat_lambda is None or (
64+
isinstance(self.timeformat_lambda, type(LAMBDA)) and self.timeformat_lambda.__name__ == LAMBDA.__name__
65+
):
66+
return True
67+
else:
68+
ValueError("Expected [None] or [lambda function] for argument [timeformat_func]")
69+
70+
@staticmethod
71+
def _default_timeformat_lambda(timestamp):
72+
return timestamp if isinstance(timestamp, datetime) else None
73+
74+
def get_time(self, id):
75+
value_str = self.get_data(id)
76+
if self.timeformat_lambda is None:
77+
return value_str
78+
79+
if isinstance(value_str, str):
80+
value = datetime.strptime(value_str, self.CONF_TIMEFORMAT)
81+
else:
82+
value = value_str
83+
84+
return self.timeformat_lambda(value)
85+
5786
@property
5887
def _new_session_args(self):
5988
return dict(
6089
session=self._session,
6190
cloud=self.cloud,
6291
api_root=self.api_root,
6392
api_version=self.api_version,
93+
timeformat_lambda=self.timeformat_lambda,
6494
)

atlassian/bitbucket/cloud/base.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
# coding=utf-8
22
import copy
3-
43
from pprint import PrettyPrinter
54

65
from ..base import BitbucketBase
76

87

98
class BitbucketCloudBase(BitbucketBase):
10-
def __init__(self, url, *args, **kwargs):
9+
def __init__(self, url, link="self", *args, **kwargs):
1110
"""
1211
Init the rest api wrapper
1312
:param url: The base url used for the rest api.
13+
:param link: Attribute to resolve a url based on input data. If None, no tries to receive an url from input data
1414
:param *args: The fixed arguments for the AtlassianRestApi.
1515
:param **kwargs: The keyword arguments for the AtlassianRestApi.
1616
@@ -23,8 +23,9 @@ def __init__(self, url, *args, **kwargs):
2323
raise ValueError(
2424
"Expected type of data is [{}], got [{}].".format(expected_type, self.get_data("type"))
2525
)
26-
if url is None:
27-
url = self.get_link("self")
26+
if url is None and link is not None:
27+
url = self.get_link(link)
28+
2829
super(BitbucketCloudBase, self).__init__(url, *args, **kwargs)
2930

3031
def __str__(self):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
""" Bitbucket Cloud common package """
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from ..base import BitbucketCloudBase
2+
3+
4+
class User(BitbucketCloudBase):
5+
def __init__(self, url, data, *args, **kwargs):
6+
super(User, self).__init__(url, *args, data=data, expected_type="user", **kwargs)
7+
8+
@property
9+
def display_name(self):
10+
return str(self.get_data("display_name"))
11+
12+
@property
13+
def nickname(self):
14+
return self.get_data("nickname")
15+
16+
@property
17+
def account_id(self):
18+
return self.get_data("account_id")
19+
20+
@property
21+
def uuid(self):
22+
return self.get_data("uuid")

atlassian/bitbucket/cloud/repositories/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .branchRestrictions import BranchRestrictions
66
from .defaultReviewers import DefaultReviewers
77
from .pipelines import Pipelines
8+
from .pullRequests import PullRequests
89

910

1011
class RepositoriesBase(BitbucketCloudBase):
@@ -57,7 +58,7 @@ def each(self, after=None, role=None, q=None, sort=None):
5758
if sort is not None:
5859
params["sort"] = sort
5960
for repository in self._get_paged(None, params):
60-
yield self._get_repository_object(repository)
61+
yield self._get_object(repository)
6162

6263

6364
class WorkspaceRepositories(RepositoriesBase):
@@ -159,6 +160,7 @@ def __init__(self, data, *args, **kwargs):
159160
self.__default_reviewers = DefaultReviewers("{}/default-reviewers".format(self.url), **self._new_session_args)
160161
self.__issues = Issues("{}/issues".format(self.url), **self._new_session_args)
161162
self.__pipelines = Pipelines("{}/pipelines".format(self.url), **self._new_session_args)
163+
self.__pullrequests = PullRequests("{}/pullrequests".format(self.url), **self._new_session_args)
162164

163165
@property
164166
def branch_restrictions(self):
@@ -176,6 +178,10 @@ def issues(self):
176178
def pipelines(self):
177179
return self.__pipelines
178180

181+
@property
182+
def pullrequests(self):
183+
return self.__pullrequests
184+
179185
@property
180186
def name(self):
181187
return self.get_data("name")

atlassian/bitbucket/cloud/repositories/defaultReviewers.py

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from requests import HTTPError
44

55
from ..base import BitbucketCloudBase
6+
from ..common.users import User
67

78

89
class DefaultReviewers(BitbucketCloudBase):
@@ -70,25 +71,9 @@ def get(self, user):
7071
return default_reviewer
7172

7273

73-
class DefaultReviewer(BitbucketCloudBase):
74+
class DefaultReviewer(User):
7475
def __init__(self, url, data, *args, **kwargs):
75-
super(DefaultReviewer, self).__init__(url, *args, data=data, expected_type="user", **kwargs)
76-
77-
@property
78-
def display_name(self):
79-
return str(self.get_data("display_name"))
80-
81-
@property
82-
def nickname(self):
83-
return self.get_data("nickname")
84-
85-
@property
86-
def account_id(self):
87-
return self.get_data("account_id")
88-
89-
@property
90-
def uuid(self):
91-
return self.get_data("uuid")
76+
super(DefaultReviewer, self).__init__(url, data, *args, **kwargs)
9277

9378
def delete(self):
9479
"""
@@ -97,4 +82,4 @@ def delete(self):
9782
data = super(DefaultReviewer, self).delete(None)
9883
if data is None or "errors" in data:
9984
return
100-
return DefaultReviewer(self.url, data, **self._new_session_args)
85+
return True

atlassian/bitbucket/cloud/repositories/issues.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,15 @@ def content(self):
113113

114114
@property
115115
def created_on(self):
116-
return self.get_data("created_on")
116+
return self.get_time("created_on")
117117

118118
@property
119119
def edited_on(self):
120-
return self.get_data("edited_on", "never edited")
120+
return self.get_time("edited_on")
121121

122122
@property
123123
def updated_on(self):
124-
return self.get_data("updated_on", "never updated")
124+
return self.get_time("updated_on")
125125

126126
def delete(self):
127127
"""

atlassian/bitbucket/cloud/repositories/pipelines.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@ def build_seconds_used(self):
113113

114114
@property
115115
def created_on(self):
116-
return self.get_data("created_on")
116+
return self.get_time("created_on")
117117

118118
@property
119119
def completed_on(self):
120-
return self.get_data("completed_on", "never completed")
120+
return self.get_time("completed_on")
121121

122122
def stop(self):
123123
return self.post("stopPipeline")
@@ -156,11 +156,11 @@ def run_number(self):
156156

157157
@property
158158
def started_on(self):
159-
return self.get_data("started_on")
159+
return self.get_time("started_on")
160160

161161
@property
162162
def completed_on(self):
163-
return self.get_data("completed_on")
163+
return self.get_time("completed_on")
164164

165165
@property
166166
def duration_in_seconds(self):

0 commit comments

Comments
 (0)