diff --git a/atlassian/bitbucket/cloud/repositories/diffstat.py b/atlassian/bitbucket/cloud/repositories/diffstat.py new file mode 100644 index 000000000..8f297ab0c --- /dev/null +++ b/atlassian/bitbucket/cloud/repositories/diffstat.py @@ -0,0 +1,76 @@ +from ..base import BitbucketCloudBase + + +class DiffStat(BitbucketCloudBase): + """ + Bitbucket Cloud repository diffstat entry. + + This represents the changes for one specific file in a diff. + + See https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-diffstat-spec-get + """ + + MODIFIED = "modified" + ADDED = "added" + REMOVED = "removed" + MERGE_CONFLICT = "merge conflict" + SUBREPO_CONFLICT = "subrepo conflict" + + def __init__(self, data, *args, **kwargs): + """See BitbucketCloudBase.""" + super(DiffStat, self).__init__(None, None, *args, data=data, expected_type="diffstat", **kwargs) + + @property + def lines_removed(self): + """Lines removed.""" + return self.get_data("lines_removed") + + @property + def lines_added(self): + """Lines added.""" + return self.get_data("lines_added") + + @property + def old(self): + """A CommitFile object, representing a file at a commit in a repository.""" + return CommitFile(self.get_data("old"), **self._new_session_args) + + @property + def new(self): + """A CommitFile object, representing a file at a commit in a repository.""" + return CommitFile(self.get_data("new"), **self._new_session_args) + + @property + def has_conflict(self): + """True if the change causes a conflict.""" + return str(self.get_data("status")) in (self.MERGE_CONFLICT, self.SUBREPO_CONFLICT) + + +class CommitFile(BitbucketCloudBase): + """ + Bitbucket Cloud repository diffstat file. + + File reference from a diffstat entry. + + See https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-diffstat-spec-get + """ + + def __init__(self, data, *args, **kwargs): + """See BitbucketCloudBase.""" + if data is None: # handles add/remove + data = {"path": None, "escaped_path": None, "links": {}, "type": "commit_file"} + super(CommitFile, self).__init__(None, None, *args, data=data, expected_type="commit_file", **kwargs) + + @property + def path(self): + """The path in the repository.""" + return self.get_data("path") + + @property + def escaped_path(self): + """ + The escaped version of the path as it appears in a diff. + + If the path does not require escaping this will be the same as path. + """ + return self.get_data("escaped_path") diff --git a/atlassian/bitbucket/cloud/repositories/pullRequests.py b/atlassian/bitbucket/cloud/repositories/pullRequests.py index e6f8fd066..1701c3131 100644 --- a/atlassian/bitbucket/cloud/repositories/pullRequests.py +++ b/atlassian/bitbucket/cloud/repositories/pullRequests.py @@ -1,6 +1,7 @@ # coding=utf-8 from ..base import BitbucketCloudBase +from .diffstat import DiffStat from ..common.users import User @@ -177,6 +178,16 @@ def destination_branch(self): """destination branch""" return self.get_data("destination")["branch"]["name"] + @property + def source_commit(self): + """Source commit.""" + return self.get_data("source")["commit"]["hash"] + + @property + def destination_commit(self): + """Destination commit.""" + return self.get_data("destination")["commit"]["hash"] + @property def comment_count(self): """number of comments""" @@ -197,6 +208,41 @@ def author(self): """User object of the author""" return User(None, self.get_data("author")) + @property + def has_conflict(self): + """Returns True if any of the changes in the PR cause conflicts.""" + for diffstat in self.diffstat(): + if diffstat.has_conflict: + return True + return False + + def diffstat(self): + """ + Returns a generator object of diffstats + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests/%7Bpull_request_id%7D/diffstat + """ + for diffstat in self._get_paged("diffstat"): + yield DiffStat(diffstat, **self._new_session_args) + + return + + def diff(self, encoding="utf-8"): + """ + Returns PR diff + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests/%7Bpull_request_id%7D/diff + """ + return str(self.get("diff", not_json_response=True), encoding=encoding) + + def patch(self, encoding="utf-8"): + """ + Returns PR patch + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests/%7Bpull_request_id%7D/patch + """ + return str(self.get("patch", not_json_response=True), encoding=encoding) + def statuses(self): """ Returns generator object of the statuses endpoint @@ -573,7 +619,7 @@ def update(self, raw_message): def delete(self): """ - Delete the pullrequest tasl. + Delete the pullrequest task. This is feature currently undocumented. """