diff --git a/atlassian/bitbucket/__init__.py b/atlassian/bitbucket/__init__.py index dd7d977b2..91ff87b2f 100644 --- a/atlassian/bitbucket/__init__.py +++ b/atlassian/bitbucket/__init__.py @@ -2538,7 +2538,6 @@ def delete_issue(self, workspace, repository_slug, id): .repositories.get(repository_slug) .issues.get(id) .delete() - .data ) @deprecated( @@ -2647,7 +2646,6 @@ def delete_branch_restriction(self, workspace, repository_slug, id): .repositories.get(repository_slug) .branch_restrictions.get(id) .delete() - .data ) @deprecated( @@ -2734,5 +2732,4 @@ def delete_default_reviewer(self, workspace, repository_slug, user): .repositories.get(repository_slug) .default_reviewers.get(user) .delete() - .data ) diff --git a/atlassian/bitbucket/base.py b/atlassian/bitbucket/base.py index ad6a77dfe..b14b41332 100644 --- a/atlassian/bitbucket/base.py +++ b/atlassian/bitbucket/base.py @@ -1,9 +1,11 @@ # coding=utf-8 +import copy import re import sys from datetime import datetime +from pprint import PrettyPrinter from ..rest_client import AtlassianRestAPI @@ -17,27 +19,37 @@ class BitbucketBase(AtlassianRestAPI): def __init__(self, url, *args, **kwargs): """ Init the rest api wrapper - :param url: The base url used for the rest api. - :param *args: The fixed arguments for the AtlassianRestApi. - :param **kwargs: The fixed arguments for the AtlassianRestApi. + + :param url: string: The base url used for the rest api. + :param *args: list: The fixed arguments for the AtlassianRestApi. + :param **kwargs: dict: The keyword arguments for the AtlassianRestApi. :return: nothing """ + self._update_data(kwargs.pop("data", {})) + if url is None: + url = self.get_link("self") + if isinstance(url, list): # Server has a list of links + url = url[0] self.timeformat_lambda = kwargs.pop("timeformat_lambda", lambda x: self._default_timeformat_lambda(x)) self._check_timeformat_lambda() super(BitbucketBase, self).__init__(url, *args, **kwargs) + def __str__(self): + return PrettyPrinter(indent=4).pformat(self.__data if self.__data else self) + def _get_paged(self, url, params=None, data=None, flags=None, trailing=None, absolute=False): """ Used to get the paged data - :param url: The url to retrieve. - :param params: The parameters (optional). - :param data: The data (optional). - :param flags: The flags (optional). - :param trailing: If True, a trailing slash is added to the url (optional). - :param absolute: If True, the url is used absolute and not relative to the root (optional). - :return: A generator for the project objects + :param url: string: The url to retrieve + :param params: dict (default is None): The parameters + :param data: dict (default is None): The data + :param flags: string[] (default is None): The flags + :param trailing: bool (default is None): If True, a trailing slash is added to the url + :param absolute: bool (default is False): If True, the url is used absolute and not relative to the root + + :return: A generator object for the data elements """ if params is None: @@ -64,7 +76,21 @@ def _get_paged(self, url, params=None, data=None, flags=None, trailing=None, abs return + @staticmethod + def _default_timeformat_lambda(timestamp): + """ + Default time format function. + + :param timestamp: datetime str: The datetime object of the parsed string or the raw value if parsing failed + + :return: timestamp if it was a datetime object, else None + """ + return timestamp if isinstance(timestamp, datetime) else None + def _check_timeformat_lambda(self): + """ + Check the lambda for for the time format. Raise an exception if the the value is wrong + """ LAMBDA = lambda: 0 # noqa: E731 if self.timeformat_lambda is None or ( isinstance(self.timeformat_lambda, type(LAMBDA)) and self.timeformat_lambda.__name__ == LAMBDA.__name__ @@ -73,11 +99,45 @@ def _check_timeformat_lambda(self): else: ValueError("Expected [None] or [lambda function] for argument [timeformat_func]") - @staticmethod - def _default_timeformat_lambda(timestamp): - return timestamp if isinstance(timestamp, datetime) else None + def _sub_url(self, url): + """ + Get the full url from a relative one. + + :param url: string: The sub url + + :return: The absolute url + """ + return self.url_joiner(self.url, url) + + @property + def data(self): + """ + Get the internal cached data. For data integrity a deep copy is returned. + + :return: A copy of the data cache + """ + return copy.copy(self.__data) + + def get_data(self, id, default=None): + """ + Get a data element from the internal data cache. For data integrity a deep copy is returned. + If data isn't present, the default value is returned. + + :param id: string: The data element to return + :param default: any (default is None): The value to return if id is not present + + :return: The requested data element + """ + return copy.copy(self.__data[id]) if id in self.__data else default def get_time(self, id): + """ + Return the time value with the expected format. + + :param id: string: The id for the time data + + :return: The time with the configured format, see timeformat_lambda. + """ value_str = self.get_data(id) if self.timeformat_lambda is None: return value_str @@ -92,8 +152,25 @@ def get_time(self, id): return self.timeformat_lambda(value) + def _update_data(self, data): + """ + Internal function to update the data. + + :param data: dict: The new data. + + :return: The updated object + """ + self.__data = data + + return self + @property def _new_session_args(self): + """ + Get the kwargs for new objects (session, root, version,...). + + :return: A dict with the kwargs for new objects + """ return dict( session=self._session, cloud=self.cloud, diff --git a/atlassian/bitbucket/cloud/base.py b/atlassian/bitbucket/cloud/base.py index 2709c13d0..9ea779638 100644 --- a/atlassian/bitbucket/cloud/base.py +++ b/atlassian/bitbucket/cloud/base.py @@ -1,48 +1,49 @@ # coding=utf-8 -import copy -from pprint import PrettyPrinter from ..base import BitbucketBase class BitbucketCloudBase(BitbucketBase): - def __init__(self, url, link="self", *args, **kwargs): + def __init__(self, url, *args, **kwargs): """ Init the rest api wrapper - :param url: The base url used for the rest api. - :param link: Attribute to resolve a url based on input data. - If None, no tries to receive an url from input data - :param *args: The fixed arguments for the AtlassianRestApi. - :param **kwargs: The keyword arguments for the AtlassianRestApi. + + :param url: string: The base url used for the rest api. + :param *args: list: The fixed arguments for the AtlassianRestApi. + :param **kwargs: dict: The keyword arguments for the AtlassianRestApi. :return: nothing """ - if "data" in kwargs: - self.__data = kwargs.pop("data") - expected_type = kwargs.pop("expected_type") - if not self.get_data("type") == expected_type: - raise ValueError( - "Expected type of data is [{}], got [{}].".format(expected_type, self.get_data("type")) - ) - if url is None and link is not None: - url = self.get_link(link) - + expected_type = kwargs.pop("expected_type", None) super(BitbucketCloudBase, self).__init__(url, *args, **kwargs) + if expected_type is not None and not expected_type == self.get_data("type"): + raise ValueError("Expected type of data is [{}], got [{}].".format(expected_type, self.get_data("type"))) + + def get_link(self, link): + """ + Get a link from the data. + + :param link: string: The link identifier - def __str__(self): - return PrettyPrinter(indent=4).pformat(self.__data) + :return: The requested link or None if it isn't present + """ + links = self.get_data("links") + if links is None or link not in links: + return None + return links[link]["href"] def _get_paged(self, url, params=None, data=None, flags=None, trailing=None, absolute=False): """ Used to get the paged data - :param url: The url to retrieve. - :param params: The parameters (optional). - :param data: The data (optional). - :param flags: The flags (optional). - :param trailing: If True, a trailing slash is added to the url (optional). - :param absolute: If True, the url is used absolute and not relative to the root (optional). - - :return: A generator for the project objects + + :param url: string: The url to retrieve + :param params: dict (default is None): The parameters + :param data: dict (default is None): The data + :param flags: string[] (default is None): The flags + :param trailing: bool (default is None): If True, a trailing slash is added to the url + :param absolute: bool (default is False): If True, the url is used absolute and not relative to the root + + :return: A generator object for the data elements """ if params is None: @@ -70,20 +71,3 @@ def _get_paged(self, url, params=None, data=None, flags=None, trailing=None, abs absolute = True return - - def update(self, **kwargs): - """ - Fields not present in the request body are ignored. - """ - self.__data = super(BitbucketBase, self).put(None, data=kwargs) - return self - - @property - def data(self): - return copy.copy(self.__data) - - def get_data(self, id, default=None): - return copy.copy(self.__data[id]) if id in self.__data else default - - def get_link(self, link): - return self.__data["links"][link]["href"] diff --git a/atlassian/bitbucket/cloud/repositories/__init__.py b/atlassian/bitbucket/cloud/repositories/__init__.py index d75ae6a77..47a08edfb 100644 --- a/atlassian/bitbucket/cloud/repositories/__init__.py +++ b/atlassian/bitbucket/cloud/repositories/__init__.py @@ -13,8 +13,6 @@ def __init__(self, url, *args, **kwargs): super(RepositoriesBase, self).__init__(url, *args, **kwargs) def _get_object(self, data): - if "errors" in data: - return return Repository(data, **self._new_session_args) @@ -162,57 +160,116 @@ def __init__(self, data, *args, **kwargs): self.__pipelines = Pipelines("{}/pipelines".format(self.url), **self._new_session_args) self.__pullrequests = PullRequests("{}/pullrequests".format(self.url), **self._new_session_args) - @property - def branch_restrictions(self): - return self.__branch_restrictions + def update(self, **kwargs): + """ + Update the repository properties. Fields not present in the request body are ignored. - @property - def default_reviewers(self): - return self.__default_reviewers + :param kwargs: dict: The data to update. - @property - def issues(self): - return self.__issues + :return: The updated repository - @property - def pipelines(self): - return self.__pipelines + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D#put + """ + return self._update_data(self.put(None, data=kwargs)) - @property - def pullrequests(self): - return self.__pullrequests + def delete(self, redirect_to=None): + """ + Delete the repostory. + + :param redirect_to: string (default is None): If a repository has been moved to a new location, use this + parameter to show users a friendly message in the Bitbucket UI + that the repository has moved to a new location. However, a GET + to this endpoint will still return a 404. + + :return: The response on success + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D#delete + """ + params = {} + if redirect_to is not None: + params["redirect_to"] = redirect_to + return super(Repository, self).delete(None, params=params) @property def name(self): + """ The repository name """ return self.get_data("name") + @name.setter + def name(self, name): + """ Setter for the repository name """ + return self.update(name=name) + @property def slug(self): + """ The repository slug """ return self.get_data("slug") @property def description(self): + """ The repository description """ return self.get_data("description") + @description.setter + def description(self, description): + """ Setter for the repository description """ + return self.update(description=description) + @property def is_private(self): + """ The repository private flag """ return self.get_data("is_private") + @is_private.setter + def is_private(self, is_private): + """ Setter for the repository private flag """ + return self.update(is_private=is_private) + @property def uuid(self): + """ The repository uuid """ return self.get_data("uuid") @property def size(self): + """ The repository size """ return self.get_data("size") @property def created_on(self): + """ The repository creation time """ return self.get_data("created_on") @property def updated_on(self): + """ The repository last update time """ return self.get_data("updated_on", "never updated") def get_avatar(self): + """ The repository avatar """ return self.get(self.get_link("avatar"), absolute=True) + + @property + def branch_restrictions(self): + """ The repository branch restrictions """ + return self.__branch_restrictions + + @property + def default_reviewers(self): + """ The repository default reviewers """ + return self.__default_reviewers + + @property + def issues(self): + """ The repository issues """ + return self.__issues + + @property + def pipelines(self): + """ The repository pipelines """ + return self.__pipelines + + @property + def pullrequests(self): + """ The repository pull requests """ + return self.__pullrequests diff --git a/atlassian/bitbucket/cloud/repositories/branchRestrictions.py b/atlassian/bitbucket/cloud/repositories/branchRestrictions.py index ce9e04d3c..be0a9739c 100644 --- a/atlassian/bitbucket/cloud/repositories/branchRestrictions.py +++ b/atlassian/bitbucket/cloud/repositories/branchRestrictions.py @@ -8,8 +8,6 @@ def __init__(self, url, *args, **kwargs): super(BranchRestrictions, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return return BranchRestriction(data, **self._new_session_args) def create( @@ -42,6 +40,8 @@ def create( Minimal: {"owner": {"username": ""}, "slug": ""} :return: The created BranchRestriction object + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/branch-restrictions#post """ if branch_match_kind == "branching_model": branch_pattern = "" @@ -78,6 +78,8 @@ def each(self, kind=None, pattern=None, q=None, sort=None): See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. :return: A generator for the BranchRestriction objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/branch-restrictions#get """ params = {} if kind is not None: @@ -100,6 +102,8 @@ def get(self, id): :param id: string: The requested issue ID :return: The requested BranchRestriction objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/branch-restrictions/%7Bid%7D#get """ return self.__get_object(super(BranchRestrictions, self).get(id)) @@ -108,43 +112,64 @@ class BranchRestriction(BitbucketCloudBase): def __init__(self, data, *args, **kwargs): super(BranchRestriction, self).__init__(None, *args, data=data, expected_type="branchrestriction", **kwargs) + def update(self, **kwargs): + """ + Update the branch restriction properties. Fields not present in the request body are ignored. + + :param kwargs: dict: The data to update. + + :return: The updated branch restriction + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/branch-restrictions/%7Bid%7D#put + """ + return self._update_data(self.put(None, data=kwargs)) + + def delete(self): + """ + Delete the branch restriction. + + :return: The response on success + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/branch-restrictions/%7Bid%7D#delete + """ + return super(BranchRestriction, self).delete(None) + @property def id(self): + """ The branch restriction id """ return str(self.get_data("id")) @property def kind(self): + """ The branch restriction kind """ return self.get_data("kind") @property def branch_match_kindstring(self): + """ The branch restriction match kindstring """ return self.get_data("branch_match_kindstring") @property def branch_typestring(self): + """ The branch restriction typestring """ return self.get_data("branch_typestring") @property def pattern(self): + """ The branch restriction pattern """ return self.get_data("pattern") @property def users(self): + """ The branch restriction users """ return self.get_data("users") @property def groups(self): + """ The branch restriction groups """ return self.get_data("groups") @property def value(self): + """ The branch restriction value """ return self.get_data("value") - - def delete(self): - """ - Deletes the branch restriction - """ - data = super(BranchRestriction, self).delete(None) - if data is None or "errors" in data: - return - return BranchRestriction(data, **self._new_session_args) diff --git a/atlassian/bitbucket/cloud/repositories/defaultReviewers.py b/atlassian/bitbucket/cloud/repositories/defaultReviewers.py index 4f3bf4431..53d5f0adb 100644 --- a/atlassian/bitbucket/cloud/repositories/defaultReviewers.py +++ b/atlassian/bitbucket/cloud/repositories/defaultReviewers.py @@ -11,8 +11,6 @@ def __init__(self, url, *args, **kwargs): super(DefaultReviewers, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return return DefaultReviewer(self.url_joiner(self.url, data["display_name"]), data, **self._new_session_args) def add(self, user): @@ -24,6 +22,8 @@ def add(self, user): :param user: string: The user to add :return: The added DefaultReviewer object + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/default-reviewers/%7Btarget_username%7D#put """ # the mention_id parameter is undocumented but if missed, leads to 400 statuses return self.__get_object(self.put(user, data={"mention_id": user})) @@ -40,6 +40,8 @@ def each(self, q=None, sort=None): See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. :return: A generator for the DefaultReviewer objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/default-reviewers#get """ params = {} if sort is not None: @@ -58,6 +60,8 @@ def get(self, user): :param user: string: The requested user name :return: The requested DefaultReviewer object, None if not a default reviewer + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/default-reviewers/%7Btarget_username%7D#get """ default_reviewer = None try: @@ -77,9 +81,10 @@ def __init__(self, url, data, *args, **kwargs): def delete(self): """ - Deletes the default reviewer + Delete the default reviewer. + + :return: The response on success + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/default-reviewers/%7Btarget_username%7D#delete """ - data = super(DefaultReviewer, self).delete(None) - if data is None or "errors" in data: - return - return True + return super(DefaultReviewer, self).delete(None) diff --git a/atlassian/bitbucket/cloud/repositories/issues.py b/atlassian/bitbucket/cloud/repositories/issues.py index a61588a3f..ac3c189ba 100644 --- a/atlassian/bitbucket/cloud/repositories/issues.py +++ b/atlassian/bitbucket/cloud/repositories/issues.py @@ -8,8 +8,6 @@ def __init__(self, url, *args, **kwargs): super(Issues, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return return Issue(data, **self._new_session_args) def create(self, title, description, kind, priority): @@ -22,6 +20,8 @@ def create(self, title, description, kind, priority): :param priority: string: One of: trivial, minor, major, critical, blocker :return: The created Issue object + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/issues/%7Bissue_id%7D#put """ data = { "title": title, @@ -41,6 +41,8 @@ def each(self, q=None, sort=None): See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. :return: A generator for the Issue objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/issues#get """ params = {} if sort is not None: @@ -59,6 +61,8 @@ def get(self, id): :param id: string: The requested issue ID :return: The requested Issue objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/issues/%7Bissue_id%7D#get """ return self.__get_object(super(Issues, self).get(id)) @@ -67,67 +71,94 @@ class Issue(BitbucketCloudBase): def __init__(self, data, *args, **kwargs): super(Issue, self).__init__(None, *args, data=data, expected_type="issue", **kwargs) + def update(self, **kwargs): + """ + Update the issue properties. Fields not present in the request body are ignored. + + :param kwargs: dict: The data to update. + + :return: The updated issue + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/issues/%7Bissue_id%7D#put + """ + return self._update_data(self.put(None, data=kwargs)) + + def delete(self): + """ + Delete the issue. + + :return: The response on success + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/issues/%7Bissue_id%7D#delete + """ + return super(Issue, self).delete(None) + @property def id(self): + """ The issue id """ return self.get_data("id") @property def title(self): + """ The issue title """ return self.get_data("title") @title.setter def title(self, title): + """ Setter for the issue title """ return self.update(title=title) @property def state(self): + """ The issue state """ return self.get_data("state") @state.setter def state(self, state): + """ Setter for the issue state """ return self.update(state=state) @property def kind(self): + """ The issue kind """ return self.get_data("kind") @kind.setter def kind(self, kind): + """ Setter for the issue kind """ return self.update(kind=kind) @property def priority(self): + """ The issue priority """ return self.get_data("priority") @priority.setter def priority(self, priority): + """ Setter for the issue property """ return self.update(priority=priority) @property def votes(self): + """ The issue votes """ return self.get_data("votes") @property def content(self): + """ The issue content """ return self.get_data("content") @property def created_on(self): + """ The issue creation time """ return self.get_time("created_on") @property def edited_on(self): + """ The issue edit time """ return self.get_time("edited_on") @property def updated_on(self): + """ The issue last update time """ return self.get_time("updated_on") - - def delete(self): - """ - Deletes the issue - """ - data = super(Issue, self).delete(None) - if data is None or "errors" in data: - return - return Issue(data, **self._new_session_args) diff --git a/atlassian/bitbucket/cloud/repositories/pipelines.py b/atlassian/bitbucket/cloud/repositories/pipelines.py index b6608ef87..1e7807347 100644 --- a/atlassian/bitbucket/cloud/repositories/pipelines.py +++ b/atlassian/bitbucket/cloud/repositories/pipelines.py @@ -10,41 +10,8 @@ def __init__(self, url, *args, **kwargs): super(Pipelines, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return return Pipeline(self.url_joiner(self.url, data["uuid"]), data, **self._new_session_args) - def each(self, q=None, sort=None): - """ - Returns the list of pipelines in this repository. - - :param q: string: Query string to narrow down the response. - See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. - :param sort: string: Name of a response property to sort results. - See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. - - :return: A generator for the Pipeline objects - """ - params = {} - if sort is not None: - params["sort"] = sort - if q is not None: - params["q"] = q - for pipeline in self._get_paged(None, trailing=True, params=params): - yield self.__get_object(pipeline) - - return - - def get(self, uuid): - """ - Returns the pipeline with the uuid in this repository. - - :param uuid: string: The requested pipeline uuid - - :return: The requested Pipeline objects - """ - return self.__get_object(super(Pipelines, self).get(uuid)) - def trigger(self, branch="master", commit=None, pattern=None, variables=None): """ Trigger a new pipeline. The following options are possible (1 and 2 @@ -64,6 +31,8 @@ def trigger(self, branch="master", commit=None, pattern=None, variables=None): }, :return: The initiated Pipeline object + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pipelines/#post """ data = { "target": { @@ -89,44 +58,89 @@ def trigger(self, branch="master", commit=None, pattern=None, variables=None): return self.__get_object(self.post(None, trailing=True, data=data)) + def each(self, q=None, sort=None): + """ + Returns the list of pipelines in this repository. + + :param q: string: Query string to narrow down the response. + See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. + :param sort: string: Name of a response property to sort results. + See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. + + :return: A generator for the Pipeline objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pipelines/#get + """ + params = {} + if sort is not None: + params["sort"] = sort + if q is not None: + params["q"] = q + for pipeline in self._get_paged(None, trailing=True, params=params): + yield self.__get_object(pipeline) + + return + + def get(self, uuid): + """ + Returns the pipeline with the uuid in this repository. + + :param uuid: string: The requested pipeline uuid + + :return: The requested Pipeline objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pipelines/%7Bpipeline_uuid%7D#get + """ + return self.__get_object(super(Pipelines, self).get(uuid)) + class Pipeline(BitbucketCloudBase): def __init__(self, url, data, *args, **kwargs): super(Pipeline, self).__init__(url, *args, data=data, expected_type="pipeline", **kwargs) def __get_object(self, data): - if "errors" in data: - return return Step("{}/steps/{}".format(self.url, data["uuid"]), data, **self._new_session_args) @property def uuid(self): + """ The pipeline uuid """ return self.get_data("uuid") @property def build_number(self): + """ The pipeline build number """ return self.get_data("build_number") @property def build_seconds_used(self): + """ The pipeline duration in seconds """ return self.get_data("build_seconds_used") @property def created_on(self): + """ The pipeline creation time """ return self.get_time("created_on") @property def completed_on(self): + """ The pipeline completion time """ return self.get_time("completed_on") def stop(self): + """ + Stop the pipeline + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pipelines/%7Bpipeline_uuid%7D/stopPipeline#post + """ return self.post("stopPipeline") def steps(self): """ - Returns the list of pipeline steps. + Get the pipeline steps. :return: A generator for the pipeline steps objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pipelines/%7Bpipeline_uuid%7D/steps/#get """ for step in self._get_paged("steps", trailing=True): yield self.__get_object(step) @@ -135,9 +149,11 @@ def steps(self): def step(self, uuid): """ - Returns the pipeline step with the uuid of this pipeline. + Get a specific pipeline step with the uuid. - :return: The requested pipeline objects + :return: The requested pipeline step objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pipelines/%7Bpipeline_uuid%7D/steps/%7Bstep_uuid%7D#get """ return self.__get_object(self.get("steps/{}".format(uuid))) @@ -148,34 +164,42 @@ def __init__(self, url, data, *args, **kwargs): @property def uuid(self): + """ The step uuid """ return self.get_data("uuid") @property def run_number(self): + """ The run number """ return self.get_data("run_number") @property def started_on(self): + """ The step start time """ return self.get_time("started_on") @property def completed_on(self): + """ The step end time """ return self.get_time("completed_on") @property def duration_in_seconds(self): + """ The step duration in seconds """ return self.get_data("duration_in_seconds") @property def state(self): + """ The step state """ return self.get_data("state") @property def setup_commands(self): + """ The step setup commands """ return self.get_data("setup_commands") @property def script_commands(self): + """ The step script commands """ return self.get_data("script_commands") def log(self, start=None, end=None): @@ -187,6 +211,8 @@ def log(self, start=None, end=None): :return: The byte representation of the log or if range is given a tuple with the overall size and the byte representation of the requested range. + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pipelines/%7Bpipeline_uuid%7D/steps/%7Bstep_uuid%7D/log#get """ headers = {"Accept": "application/octet-stream"} if ((start is not None) and (end is None)) or ((start is None) and (end is not None)): diff --git a/atlassian/bitbucket/cloud/repositories/pullRequests.py b/atlassian/bitbucket/cloud/repositories/pullRequests.py index 8fd6e78d1..51725d472 100644 --- a/atlassian/bitbucket/cloud/repositories/pullRequests.py +++ b/atlassian/bitbucket/cloud/repositories/pullRequests.py @@ -7,48 +7,13 @@ class PullRequests(BitbucketCloudBase): """ Bitbucket Cloud pull requests - - API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests#get """ def __init__(self, url, *args, **kwargs): super(PullRequests, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return - return PullRequest(self.url_joiner(self.url, data["id"]), data, **self._new_session_args) - - def each(self, q=None, sort=None): - """ - Returns the list of pull requests in this repository. - - :param q: string: Query string to narrow down the response. - See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. - :param sort: string: Name of a response property to sort results. - See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. - - :return: A generator for the PullRequest objects - """ - params = {} - if sort is not None: - params["sort"] = sort - if q is not None: - params["q"] = q - for pr in self._get_paged(None, trailing=True, params=params): - yield self.__get_object(pr) - - return - - def get(self, id): - """ - Returns the pull requests with the requested id in this repository. - - :param id: int: The requested pull request id - - :return: The requested PullRequest object - """ - return self.__get_object(super(PullRequests, self).get(id)) + return PullRequest(data, **self._new_session_args) def create( self, @@ -69,6 +34,7 @@ def create( :param description: string: pull request description :param close_source_branch: bool: specifies if the source branch should be closed upon merging :param reviewers: list: list of user uuids in curly brackets + :return: Pull Request Object API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests#post @@ -84,9 +50,43 @@ def create( } if destination_branch: data["destination"] = {"branch": {"name": destination_branch}} - response = self.post(self.url, data, absolute=True) - return PullRequest(response["links"]["self"], response, **self._new_session_args) + return self.__get_object(self.post(None, data)) + + def each(self, q=None, sort=None): + """ + Returns the list of pull requests in this repository. + + :param q: string: Query string to narrow down the response. + See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. + :param sort: string: Name of a response property to sort results. + See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. + + :return: A generator for the PullRequest objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests#get + """ + params = {} + if sort is not None: + params["sort"] = sort + if q is not None: + params["q"] = q + for pr in self._get_paged(None, trailing=True, params=params): + yield self.__get_object(pr) + + return + + def get(self, id): + """ + Returns the pull requests with the requested id in this repository. + + :param id: int: The requested pull request id + + :return: The requested PullRequest object + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests/%7Bpull_request_id%7D#get + """ + return self.__get_object(super(PullRequests, self).get(id)) class PullRequest(BitbucketCloudBase): @@ -109,8 +109,8 @@ class PullRequest(BitbucketCloudBase): STATE_MERGED = "MERGED" STATE_SUPERSEDED = "SUPERSEDED" - def __init__(self, url, data, *args, **kwargs): - super(PullRequest, self).__init__(url, *args, data=data, expected_type="pullrequest", **kwargs) + def __init__(self, data, *args, **kwargs): + super(PullRequest, self).__init__(None, *args, data=data, expected_type="pullrequest", **kwargs) def _check_if_open(self): if not self.is_open: @@ -245,7 +245,7 @@ def unapprove(self): API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests/%7Bpull_request_id%7D/approve#delete """ self._check_if_open() - return self.delete("approve") + return super(BitbucketCloudBase, self).delete("approve") def request_changes(self): """ @@ -264,7 +264,7 @@ def unrequest_changes(self): API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests/%7Bpull_request_id%7D/request-changes#delete """ self._check_if_open() - return self.delete("request-changes") + return super(BitbucketCloudBase, self).delete("request-changes") def decline(self): """ diff --git a/atlassian/bitbucket/cloud/workspaces/__init__.py b/atlassian/bitbucket/cloud/workspaces/__init__.py index 77db24515..75a78df39 100644 --- a/atlassian/bitbucket/cloud/workspaces/__init__.py +++ b/atlassian/bitbucket/cloud/workspaces/__init__.py @@ -11,24 +11,22 @@ def __init__(self, url, *args, **kwargs): super(Workspaces, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return return Workspace(data, **self._new_session_args) def each(self, role=None, q=None, sort=None): """ Get all workspaces matching the criteria. - :param role: string: Filters the workspaces based on the authenticated user"s role on each workspace. - * member: returns a list of all the workspaces which the caller is a member of - at least one workspace group or repository - * collaborator: returns a list of workspaces which the caller has write access - to at least one repository in the workspace - * owner: returns a list of workspaces which the caller has administrator access - :param q: string: Query string to narrow down the response. - See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. - :param sort: string: Name of a response property to sort results. - See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. + :param role: string (default is None): Filters the workspaces based on the authenticated user"s role on each workspace. + * member: returns a list of all the workspaces which the caller is a member of + at least one workspace group or repository + * collaborator: returns a list of workspaces which the caller has write access + to at least one repository in the workspace + * owner: returns a list of workspaces which the caller has administrator access + :param q: string (default is None): Query string to narrow down the response. + See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. + :param sort: string (default is None): Name of a response property to sort results. + See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. :return: A generator for the Workspace objects """ @@ -62,36 +60,51 @@ def __init__(self, data, *args, **kwargs): self.__projects = Projects(self.get_link("projects"), **self._new_session_args) self.__repositories = WorkspaceRepositories(self.get_link("repositories"), **self._new_session_args) - @property - def projects(self): - return self.__projects - - @property - def repositories(self): - return self.__repositories - @property def name(self): + """ The workspace name """ return self.get_data("name") + @name.setter + def name(self, name): + """ Setter for the workspace name """ + return self.update(name=name) + @property def slug(self): + """ The workspace slug """ return self.get_data("slug") + @property def uuid(self): + """ The workspace uuid """ return self.get_data("uuid") @property def is_private(self): + """ The workspace private flag """ return self.get_data("is_private") @property def created_on(self): + """ The workspace creation time """ return self.get_data("created_on") @property def updated_on(self): + """ The workspace last update time """ return self.get_data("updated_on", "never updated") def get_avatar(self): + """ The project avatar """ return self.get(self.get_link("avatar"), absolute=True) + + @property + def projects(self): + """ The workspace projects """ + return self.__projects + + @property + def repositories(self): + """ The workspace repositories """ + return self.__repositories diff --git a/atlassian/bitbucket/cloud/workspaces/projects.py b/atlassian/bitbucket/cloud/workspaces/projects.py index 7279abd1b..6125c3441 100644 --- a/atlassian/bitbucket/cloud/workspaces/projects.py +++ b/atlassian/bitbucket/cloud/workspaces/projects.py @@ -10,8 +10,6 @@ def __init__(self, url, *args, **kwargs): super(Projects, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return return Project(data, **self._new_session_args) def create(self, name, key, description, is_private=True, avatar=None): @@ -31,13 +29,15 @@ def create(self, name, key, description, is_private=True, avatar=None): is_private=False ) - :param name: string: The name of the project. - :param key: string: The key of the project. - :param description: string: The description of the project. - :param is_private: boolean (True): True if it is a private project. - :param avatar: string (None): The avatar of the project. + :param name: string: The name of the project. + :param key: string: The key of the project. + :param description: string: The description of the project. + :param is_private: bool (default is True): True if it is a private project. + :param avatar: string (default is None): The avatar of the project. :return: The created project object + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/workspaces/%7Bworkspace%7D/projects#post """ data = { "name": name, @@ -51,13 +51,14 @@ def each(self, q=None, sort=None): """ Get all projects in the workspace matching the criteria. - - :param q: string: Query string to narrow down the response. - See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. - :param sort: string: Name of a response property to sort results. - See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. + :param q: string (default is None): Query string to narrow down the response. + See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. + :param sort: string (default is None): Name of a response property to sort results. + See https://developer.atlassian.com/bitbucket/api/2/reference/meta/filtering for details. :return: A generator for the project objects + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/workspaces/%7Bworkspace%7D/projects#get """ params = {} if sort is not None: @@ -73,8 +74,8 @@ def get(self, project, by="key"): """ Returns the requested project - :param project: string: The requested project. - :param by: string: How to interprate project, can be 'key' or 'name'. + :param project: string: The requested project. + :param by: string (default is "key"): How to interprate project, can be 'key' or 'name'. :return: The requested Project object """ @@ -100,58 +101,86 @@ def __init__(self, data, *args, **kwargs): url = '{}/?q=project.key="{}"'.format(workspace["links"]["self"], workspace["slug"]) self.__repositories = ProjectRepositories(url, **self._new_session_args) - @property - def repositories(self): - return self.__repositories + def update(self, **kwargs): + """ + Update the project properties. Fields not present in the request body are ignored. + + :param kwargs: dict: The data to update. + + :return: The updated project + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/workspaces/%7Bworkspace%7D/projects/%7Bproject_key%7D#put + """ + return self._update_data(self.put(None, data=kwargs)) + + def delete(self): + """ + Delete the project. + + :return: The response on success + + API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/workspaces/%7Bworkspace%7D/projects/%7Bproject_key%7D#delete + """ + data = super(Project, self).delete(None) + if data is None or "errors" in data: + return + return data @property def name(self): + """ The project name """ return self.get_data("name") @name.setter def name(self, name): + """ Setter for the project name """ return self.update(name=name) @property def key(self): + """ The project key """ return self.get_data("key") @key.setter def key(self, key): + """ Setter for the project key """ return self.update(key=key) @property def description(self): + """ The project description """ return self.get_data("description") @description.setter def description(self, description): + """ Setter for the project description """ return self.update(description=description) @property def is_private(self): + """ The project private flag """ return self.get_data("is_private") @is_private.setter def is_private(self, is_private): + """ Setter for the project private flag """ return self.update(is_private=is_private) @property def created_on(self): + """ The project creation time """ return self.get_data("created_on") @property def updated_on(self): + """ The project last update time """ return self.get_data("updated_on", "never updated") def get_avatar(self): + """ The project avatar """ return self.get(self.get_link("avatar"), absolute=True) - def delete(self): - """ - Delete the project - """ - data = super(Project, self).delete(None) - if data is None or "errors" in data: - return - return data + @property + def repositories(self): + """ The project repositories """ + return self.__repositories diff --git a/atlassian/bitbucket/server/base.py b/atlassian/bitbucket/server/base.py index e1f220526..90c8ffb76 100644 --- a/atlassian/bitbucket/server/base.py +++ b/atlassian/bitbucket/server/base.py @@ -1,49 +1,35 @@ # coding=utf-8 -import copy - -from pprint import PrettyPrinter from ..base import BitbucketBase class BitbucketServerBase(BitbucketBase): - def __init__(self, url, *args, **kwargs): - """ - Init the rest api wrapper - :param url: The base url used for the rest api. - :param *args: The fixed arguments for the AtlassianRestApi. - :param **kwargs: The keyword arguments for the AtlassianRestApi. - - :return: nothing + def get_link(self, link): """ - self.__can_delete = kwargs.pop("can_delete", False) - self.__can_update = kwargs.pop("can_update", False) - if "data" in kwargs: - self.__data = kwargs.pop("data") - if url is None and "links" in self.__data: - url = self.get_link("self")[0] - super(BitbucketServerBase, self).__init__(url, *args, **kwargs) + Get a link from the data. - def __str__(self): - return PrettyPrinter(indent=4).pformat(self.__data) + :param link: string: The link identifier - def _sub_url(self, url): - return self.url_joiner(self.url, url) + :return: The requested list of links or None if it isn't present + """ + links = self.get_data("links") + if links is None or link not in links: + return None + return [x["href"] for x in links[link]] def _get_paged(self, url, params=None, data=None, flags=None, trailing=False, absolute=False): """ Used to get the paged data - :param url: The url to retrieve. - :param params: The parameters (optional). - :param data: The data (optional). - :param flags: The flags (optional). - :param trailing: If True, a trailing slash is added to the url (optional). - :param absolute: If True, the url is used absolute and not relative to the root (optional). + :param url: string: The url to retrieve + :param params: dict (default is None): The parameters + :param data: dict (default is None): The data + :param flags: string[] (default is None): The flags + :param trailing: bool (default is None): If True, a trailing slash is added to the url + :param absolute: bool (default is False): If True, the url is used absolute and not relative to the root - :return: A generator for the project objects + :return: A generator object for the data elements """ - if params is None: params = {} @@ -67,41 +53,3 @@ def _get_paged(self, url, params=None, data=None, flags=None, trailing=False, ab params["start"] = response.get("nextPageStart") return - - def update(self, **kwargs): - """ - Update the data. Fields not present in the request body are ignored. - - :return: True if the update was successful - """ - if self.__can_update is False: - raise NotImplementedError("Update not implemented for this object type.") - data = self.put(None, data=kwargs) - if "errors" in data: - return - self.__data = data - - return True - - def delete(self): - """ - Delete the element. - - :return: True if delete was successful - """ - if self.__can_delete is False: - raise NotImplementedError("Delete not implemented for this object type.") - data = super(BitbucketServerBase, self).delete(None) - if data is None or "errors" in data: - return - return True - - @property - def data(self): - return copy.copy(self.__data) - - def get_data(self, id, default=None): - return copy.copy(self.__data[id]) if id in self.__data else default - - def get_link(self, link): - return [x["href"] for x in self.__data["links"][link]] diff --git a/atlassian/bitbucket/server/common/__init__.py b/atlassian/bitbucket/server/common/__init__.py index de99779a7..438d6ab0f 100644 --- a/atlassian/bitbucket/server/common/__init__.py +++ b/atlassian/bitbucket/server/common/__init__.py @@ -1,209 +1 @@ -# coding=utf-8 - -from ..base import BitbucketServerBase - - -class Permissions(BitbucketServerBase): - ADMIN = "ADMIN" - WRITE = "WRITE" - READ = "READ" - - def __init__(self, url, permission_prefix, *args, **kwargs): - self.__permission_prefix = permission_prefix - super(Permissions, self).__init__(url, *args, can_delete=True, **kwargs) - - def __permission(self, permission): - return "{}_{}".format(self.__permission_prefix, permission) - - def admin(self, name): - """ - Add the admin permission for a group/user. - """ - return self.add(name, self.__permission(self.ADMIN)) - - def write(self, name): - """ - Add the write permission for a group/user. - """ - return self.add(name, self.__permission(self.WRITE)) - - def read(self, name): - """ - Add the read permission for a group/user. - """ - return self.add(name, self.__permission(self.READ)) - - def add(self, name, permission): - """ - Add the permission for a group/user. - - For project groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp160 - For project users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp166 - For repository groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp282 - For repository users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp288 - - :param name: string: The names of the groups/users - :param permission: string: The permission to grant. - - :return: True on success - """ - data = self.put(name, permission) - if "errors" in data: - return - return True - - def each(self, filter=None): - """ - Get all groups/users. - - For project groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp159 - For project users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp165 - For repository groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp280 - For repository users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp286 - - :params filter: string: If specified only group/user names containing the supplied string will be returned - - :return: A generator for the group/user permission objects - """ - params = {} - if filter is not None: - params["filter"] = filter - for entry in self._get_paged(None, params=params): - entry = self._get_object(entry) - entry.url = self.url - yield entry - - def each_none(self, filter=None): - """ - Get all not granted groups/users. - - For project groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp163 - For project users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp169 - For repository groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp284 - For repository users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp290 - - :params filter: string: If specified only group/user names containing the supplied string will be returned - - :return: A generator for the group/user permission objects - """ - params = {} - if filter is not None: - params["filter"] = filter - for entry in self._get_paged("none", params=params): - yield self._get_object(entry) - - def get(self, name): - """ - Returns the requested group/user - - :param name: string: The requested element name. - - :return: The requested group/user object - """ - for entry in self.each(filter=name): - if entry.name == name: - return entry - - raise Exception("Unknown group/user '{}'".format(name)) - - -class Groups(Permissions): - def __init__(self, url, permission_prefix, *args, **kwargs): - super(Groups, self).__init__(url, permission_prefix, *args, **kwargs) - - def _get_object(self, data): - if "errors" in data: - return - return Group(data, **self._new_session_args) - - -class Users(Permissions): - def __init__(self, url, permission_prefix, *args, **kwargs): - super(Users, self).__init__(url, permission_prefix, *args, **kwargs) - - def _get_object(self, data): - if "errors" in data: - return - return User(data, **self._new_session_args) - - -class PermissionBase(BitbucketServerBase): - @property - def permission(self): - if self.url is None: - raise NotImplementedError("Permission not implemented for this object type.") - return self.get_data("permission") - - @property - def is_admin(self): - return True if self.permission == Permissions.ADMIN else False - - @property - def is_write(self): - return True if self.permission == Permissions.WRITE else False - - @property - def is_read(self): - return True if self.permission == Permissions.READ else False - - @property - def can_write(self): - return True if self.permission in (Permissions.ADMIN, Permissions.WRITE) else False - - def delete(self): - """ - Delete the element. - - :return: True if the permission was deleted - """ - if self.url is None: - raise NotImplementedError("Delete not implemented for this object type.") - data = super(BitbucketServerBase, self).delete(None, params={"name": self.name}) - if data is None or "errors" in data: - return - return True - - -class Group(PermissionBase): - def __init__(self, data, *args, **kwargs): - super(Group, self).__init__(None, *args, data=data, **kwargs) - - @property - def name(self): - if self.url is None: - return self.get_data("name") - return self.get_data("group")["name"] - - -class User(PermissionBase): - def __init__(self, data, *args, **kwargs): - super(User, self).__init__(None, *args, data=data, **kwargs) - - def __userdata(self, key): - if self.url is None: - return self.get_data(key) - return self.get_data("user")[key] - - @property - def name(self): - return self.__userdata("name") - - @property - def email(self): - return self.__userdata("emailAddress") - - @property - def displayname(self): - return self.__userdata("displayName") - - @property - def active(self): - return self.__userdata("active") - - @property - def slug(self): - return self.__userdata("slug") - - @property - def id(self): - return self.__userdata("id") +""" Bitbucket Server common package """ \ No newline at end of file diff --git a/atlassian/bitbucket/server/common/permissions.py b/atlassian/bitbucket/server/common/permissions.py new file mode 100644 index 000000000..c22b8eb8f --- /dev/null +++ b/atlassian/bitbucket/server/common/permissions.py @@ -0,0 +1,220 @@ +# coding=utf-8 + +from ..base import BitbucketServerBase + + +class Permissions(BitbucketServerBase): + ADMIN = "ADMIN" + WRITE = "WRITE" + READ = "READ" + + def __init__(self, url, permission_prefix, *args, **kwargs): + self.__permission_prefix = permission_prefix + super(Permissions, self).__init__(url, *args, **kwargs) + + def __permission(self, permission): + """ Internal function to get the permission for the put request. """ + return "{}_{}".format(self.__permission_prefix, permission) + + def admin(self, name): + """ + Add the admin permission for a group/user. + """ + return self.add(name, self.__permission(self.ADMIN)) + + def write(self, name): + """ + Add the write permission for a group/user. + """ + return self.add(name, self.__permission(self.WRITE)) + + def read(self, name): + """ + Add the read permission for a group/user. + """ + return self.add(name, self.__permission(self.READ)) + + def add(self, name, permission): + """ + Add the permission for a group/user. + + :param name: string: The names of the groups/users + :param permission: string: The permission to grant. + + API docs: + - For project groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp160 + - For project users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp166 + - For repository groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp282 + - For repository users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp288 + """ + self.put(name, permission) + return + + def each(self, filter=None): + """ + Get all groups/users. + + :params filter: string: If specified only group/user names containing the supplied string will be returned + + :return: A generator for the group/user permission objects + + API docs: + - For project groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp159 + - For project users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp165 + - For repository groupss see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp280 + - For repository users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp286 + """ + params = {} + if filter is not None: + params["filter"] = filter + for entry in self._get_paged(None, params=params): + entry = self._get_object(entry) + entry.url = self.url + yield entry + + def each_none(self, filter=None): + """ + Get all not granted groups/users. + + :params filter: string: If specified only group/user names containing the supplied string will be returned + + :return: A generator for the group/user permission objects + + API docs: + - For project groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp163 + - For project users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp169 + - For repository groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp284 + - For repository users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp290 + """ + params = {} + if filter is not None: + params["filter"] = filter + for entry in self._get_paged("none", params=params): + yield self._get_object(entry) + + def get(self, name): + """ + Returns the requested group/user + + :param name: string: The requested element name. + + :return: The requested group/user object + """ + for entry in self.each(filter=name): + if entry.name == name: + return entry + + raise Exception("Unknown group/user '{}'".format(name)) + + +class Groups(Permissions): + def __init__(self, url, permission_prefix, *args, **kwargs): + super(Groups, self).__init__(url, permission_prefix, *args, **kwargs) + + def _get_object(self, data): + return Group(data, **self._new_session_args) + + +class Users(Permissions): + def __init__(self, url, permission_prefix, *args, **kwargs): + super(Users, self).__init__(url, permission_prefix, *args, **kwargs) + + def _get_object(self, data): + return User(data, **self._new_session_args) + + +class PermissionBase(BitbucketServerBase): + def delete(self): + """ + Delete the permission. + + :return: The response on success + """ + if self.url is None: + raise NotImplementedError("Delete not implemented for this object type.") + return super(BitbucketServerBase, self).delete(None, params={"name": self.name}) + + @property + def permission(self): + if self.url is None: + raise NotImplementedError("Pemission not implemented for this object type.") + return self.get_data("permission") + + @property + def is_admin(self): + """ True if group/user is admin """ + return True if self.permission == Permissions.ADMIN else False + + @property + def is_write(self): + """ True if group/user has write permission """ + return True if self.permission == Permissions.WRITE else False + + @property + def is_read(self): + """ True if group/user has read premission """ + return True if self.permission == Permissions.READ else False + + @property + def can_write(self): + """ True if group/user can write """ + return True if self.permission in (Permissions.ADMIN, Permissions.WRITE) else False + + +class Group(PermissionBase): + def __init__(self, data, *args, **kwargs): + super(Group, self).__init__(None, *args, data=data, **kwargs) + + @property + def name(self): + """ The name of the group """ + if self.url is None: + return self.get_data("name") + return self.get_data("group")["name"] + + +class User(PermissionBase): + def __init__(self, data, *args, **kwargs): + super(User, self).__init__(None, *args, data=data, **kwargs) + + def __userdata(self, key): + """ + Internal getter for the user data. + + :param key: string: The user data to get + + :return: The requested user data + """ + if self.url is None: + return self.get_data(key) + return self.get_data("user")[key] + + @property + def name(self): + """ The name of the user """ + return self.__userdata("name") + + @property + def email(self): + """ The email of the user """ + return self.__userdata("emailAddress") + + @property + def displayname(self): + """ The displayname of the user """ + return self.__userdata("displayName") + + @property + def active(self): + """ The active flag of the user """ + return self.__userdata("active") + + @property + def slug(self): + """ The slug of the user """ + return self.__userdata("slug") + + @property + def id(self): + """ The id of the user """ + return self.__userdata("id") diff --git a/atlassian/bitbucket/server/globalPermissions.py b/atlassian/bitbucket/server/globalPermissions.py index a75e3c435..2db8a47d1 100644 --- a/atlassian/bitbucket/server/globalPermissions.py +++ b/atlassian/bitbucket/server/globalPermissions.py @@ -10,7 +10,7 @@ class GlobalPermissions(BitbucketServerBase): SYS_ADMIN = "SYS_ADMIN" def __init__(self, url, *args, **kwargs): - super(GlobalPermissions, self).__init__(url, *args, can_delete=True, **kwargs) + super(GlobalPermissions, self).__init__(url, *args, **kwargs) def licensed_user(self, name): """ @@ -40,29 +40,27 @@ def add(self, name, permission): """ Add the permission for a group/user. - For groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp64 - For users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp70 - :param name: string: The names of the groups/users :param permission: string: The permission to grant. - :return: True on success + API docs: + - For groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp64 + - For users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp70 """ - data = self.put(name, permission) - if "errors" in data: - return - return True + self.put(name, permission) + return def each(self, filter=None): """ Get all groups/users. - For groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp63 - For users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp69 - :params filter: string: If specified only group/user names containing the supplied string will be returned :return: A generator for the group/user permission objects + + API docs: + - For groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp63 + - For users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp69 """ params = {} if filter is not None: @@ -76,12 +74,13 @@ def each_none(self, filter=None): """ Get all not granted groups/users. - For groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp67 - For users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp73 - :params filter: string: If specified only group/user names containing the supplied string will be returned :return: A generator for the group/user permission objects + + API docs: + - For groups see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp67 + - For users see https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp73 """ params = {} if filter is not None: @@ -109,8 +108,6 @@ def __init__(self, url, *args, **kwargs): super(Groups, self).__init__(url, *args, **kwargs) def _get_object(self, data): - if "errors" in data: - return return Group(data, **self._new_session_args) @@ -119,8 +116,6 @@ def __init__(self, url, *args, **kwargs): super(Users, self).__init__(url, *args, **kwargs) def _get_object(self, data): - if "errors" in data: - return return User(data, **self._new_session_args) @@ -149,16 +144,13 @@ def is_sys_admin(self): def delete(self): """ - Delete the element. + Delete the permission. - :return: True if the permission was deleted + :return: The response on success """ if self.url is None: raise NotImplementedError("Delete not implemented for this object type.") - data = super(BitbucketServerBase, self).delete(None, params={"name": self.name}) - if data is None or "errors" in data: - return - return True + return super(PermissionBase, self).delete(None, params={"name": self.name}) class Group(PermissionBase): diff --git a/atlassian/bitbucket/server/projects/__init__.py b/atlassian/bitbucket/server/projects/__init__.py index 734d0f418..214998656 100644 --- a/atlassian/bitbucket/server/projects/__init__.py +++ b/atlassian/bitbucket/server/projects/__init__.py @@ -2,7 +2,7 @@ from .repos import Repositories from ..base import BitbucketServerBase -from ..common import Groups, Users +from ..common.permissions import Groups, Users class Projects(BitbucketServerBase): @@ -10,8 +10,6 @@ def __init__(self, url, *args, **kwargs): super(Projects, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return return Project(data, **self._new_session_args) def create(self, name, key, description, avatar=None): @@ -29,14 +27,14 @@ def create(self, name, key, description, avatar=None): avatar="http://i.imgur.com/72tRx4w.gif" ) - See https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp148 - :param name: string: The name of the project. :param key: string: The key of the project. :param description: string: The description of the project. :param avatar: string: The avatar of the project. :return: The created project object + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp148 """ return self.__get_object(self.post(None, data={"name": name, "key": key, "description": description})) @@ -44,12 +42,12 @@ def each(self, name=None, permission=None): """ Get all projects matching the criteria. - See https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp149 - :param name: string: Name to filter by. :param permission: string: Permission to filter by. :return: A generator for the project objects + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp149 """ params = {} if name is not None: @@ -69,6 +67,8 @@ def get(self, project, by="key"): :param by: string: How to interprate project, can be 'key' or 'name'. :return: The requested Project object + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp153 """ if by == "key": return self.__get_object(super(Projects, self).get(project)) @@ -84,61 +84,105 @@ def get(self, project, by="key"): class Project(BitbucketServerBase): def __init__(self, data, *args, **kwargs): - super(Project, self).__init__(None, *args, data=data, can_update=True, can_delete=True, **kwargs) + super(Project, self).__init__(None, *args, data=data, **kwargs) self.__groups = Groups(self._sub_url("permissions/groups"), "PROJECT", **self._new_session_args) self.__users = Users(self._sub_url("permissions/users"), "PROJECT", **self._new_session_args) self.__repos = Repositories(self._sub_url("repos"), **self._new_session_args) + def delete(self): + """ + Delete the project. + + :return: The response on success + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp151 + """ + return super(Project, self).delete(None) + + def update(self, **kwargs): + """ + Update the project properties. Fields not present in the request body are ignored. + + :param kwargs: dict: The data to update. + + :return: The updated project + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp152 + """ + return self._update_data(self.put(None, data=kwargs)) + + @property + def id(self): + """ The project identifier """ + return self.get_data("id") + + @property + def type(self): + """ The project type """ + return self.get_data("type") + @property def name(self): + """ The project name """ return self.get_data("name") @name.setter def name(self, name): - return self.update(data={"name": name}) + """ Setter for the project name """ + return self.update(name=name) @property def key(self): + """ The project key """ return self.get_data("key") @key.setter def key(self, key): - return self.update(data={"key": key}) + """ Setter for the project key """ + return self.update(key=key) @property def description(self): + """ The project description """ return self.get_data("description") @description.setter def description(self, description): - return self.update(data={"description": description}) + """ Setter for the project description """ + return self.update(description=description) @property def public(self): + """ The project public flag """ return self.get_data("public") @public.setter def public(self, public): - return self.update(data={"public": public}) - - @property - def id(self): - return self.get_data("id") - - @property - def type(self): - return self.get_data("type") + """ Setter for the project public flag """ + return self.update(public=public) def get_avatar(self, s=0): """ + Get the avatar. + :param s: int (default 0): The desired size of the image. The server will return an image as close as possible to the specified size. + + :return: The response on success + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp155 """ return self.get("avatar.png", params={"s": s}) def set_avatar(self, avatar): """ + Set the avatar. + :param avatar: See function create for examples. + + :return: The response on success + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp156 """ return self.post("avatar.png", data={"avatar": avatar}) @@ -146,7 +190,7 @@ def set_avatar(self, avatar): def groups(self): """ Property to access the project groups - Reference: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp158 + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp158 """ return self.__groups @@ -154,7 +198,7 @@ def groups(self): def users(self): """ Property to access the project groups - Reference: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp164 + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp164 """ return self.__users @@ -162,6 +206,6 @@ def users(self): def repos(self): """ Property to access the repositories - Reference: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp173 + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp173 """ return self.__repos diff --git a/atlassian/bitbucket/server/projects/repos/__init__.py b/atlassian/bitbucket/server/projects/repos/__init__.py index f8d82b4fd..ef655e3ab 100644 --- a/atlassian/bitbucket/server/projects/repos/__init__.py +++ b/atlassian/bitbucket/server/projects/repos/__init__.py @@ -1,7 +1,7 @@ # coding=utf-8 from ...base import BitbucketServerBase -from ...common import Groups, Users +from ...common.permissions import Groups, Users class Repositories(BitbucketServerBase): @@ -9,8 +9,6 @@ def __init__(self, url, *args, **kwargs): super(Repositories, self).__init__(url, *args, **kwargs) def __get_object(self, data): - if "errors" in data: - return return Repository(data, **self._new_session_args) def create(self, name): @@ -60,65 +58,95 @@ def get(self, repository, by="slug"): class Repository(BitbucketServerBase): def __init__(self, data, *args, **kwargs): - super(Repository, self).__init__(None, *args, data=data, can_update=True, can_delete=True, **kwargs) + super(Repository, self).__init__(None, *args, data=data, **kwargs) self.__groups = Groups(self._sub_url("permissions/groups"), "REPO", **self._new_session_args) self.__users = Users(self._sub_url("permissions/users"), "REPO", **self._new_session_args) def __get_object(self, data): - if "errors" in data: - return return Repository(data, **self._new_session_args) + def update(self, **kwargs): + """ + Update the repository properties. Fields not present in the request body are ignored. + + :param kwargs: dict: The data to update. + + :return: The updated repository + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp180 + """ + return self._update_data(self.put(None, data=kwargs)) + + def delete(self): + """ + Delete the repostory. + + :return: The response on success + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp177 + """ + return super(Repository, self).delete(None) + @property def id(self): + """ The repository identifier """ return self.get_data("id") @property def name(self): + """ The repository name """ return self.get_data("name") @name.setter def name(self, name): - return self.update(data={"name": name}) + """ Setter for the repository name """ + return self.update(name=name) @property def slug(self): + """ The repository slug """ return self.get_data("slug") @property def description(self): + """ The repository description """ return self.get_data("description") @description.setter def description(self, description): - return self.update(data={"description": description}) + """ Setter for the repository description """ + return self.update(description=description) @property def public(self): + """ The repository public flag """ return self.get_data("public") @public.setter def public(self, public): - return self.update(data={"public": public}) + """ Setter for the repository public flag """ + return self.update(public=public) @property def forkable(self): + """ The repository forkable flag """ return self.get_data("forkable") @forkable.setter def forkable(self, forkable): - return self.update(data={"forkable": forkable}) + """ Setter for the repository forkable flag """ + return self.update(forkable=forkable) def contributing(self, at=None, markup=None): """ Get the contributing guidelines. - https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp183 - :param at: string: Optional, the commit to get the contributing guideline from. :param markup: boolean: Optional, If set to true, the rendered content is returned as HTML. - :return: The text content of the contributing guidelines. + :return: The text content of the contributing guidlines. + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp183 """ params = {} if at is not None: @@ -132,12 +160,12 @@ def license(self, at=None, markup=None): """ Get the license file. - https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp191 - :param at: string: Optional, the commit to get the license file from. :param markup: boolean: Optional, If set to true, the rendered content is returned as HTML. :return: The text content of the license file. + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp191 """ params = {} if at is not None: @@ -151,12 +179,12 @@ def readme(self, at=None, markup=None): """ Get the readme file. - https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp194 - :param at: string: Optional, the commit to get the readme file from. :param markup: boolean: Optional, If set to true, the rendered content is returned as HTML. :return: The text content of the readme file. + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp194 """ params = {} if at is not None: @@ -171,9 +199,9 @@ def default_branch(self): """ Get the default branch. - https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp185 - :return: The default branch (displayId without refs/heads/). + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp185 """ return self.get("default-branch")["displayId"] @@ -182,23 +210,19 @@ def default_branch(self, branch): """ Set the default branch. - https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp186 - :param id: string: The default branch to set (without refs/heads/). - :return: True on success. + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp186 """ - response = self.put("default-branch", data={"id": "refs/heads/{}".format(branch)}) - if "errors" in response: - return - return True + self.put("default-branch", data={"id": "refs/heads/{}".format(branch)}) def forks(self): """ Get all forks. - https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp188 - :return: A generator object for the forks. + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp188 """ for fork in self._get_paged("forks"): yield fork @@ -207,9 +231,9 @@ def related(self): """ Get all related repositories. - See https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp195 - :return: A generator for the related repository objects + + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp195 """ for repository in self._get_paged("related"): yield self.__get_object(repository) @@ -218,7 +242,7 @@ def related(self): def groups(self): """ Property to access the project groups: - Reference: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp279 + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp279 """ return self.__groups @@ -226,6 +250,6 @@ def groups(self): def users(self): """ Property to access the project groups - Reference: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp285 + API docs: https://docs.atlassian.com/bitbucket-server/rest/7.8.0/bitbucket-rest.html#idp285 """ return self.__users diff --git a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/GET b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/GET index 4d6ac9a4c..cea4917aa 100644 --- a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/GET +++ b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/GET @@ -11,22 +11,24 @@ responses[None] = { "type": "pullrequest", "description": "PRDescription", "links": { - "decline": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, + "decline": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, "diffstat": { - "href": "repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:16182c4698fb%0D1fbd047cd123?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:16182c4698fb%0D1fbd047cd123?from_pullrequest_id=1" }, - "commits": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, - "self": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1"}, - "comments": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, - "merge": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, + "commits": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1"}, + "comments": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, + "merge": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/pull-requests/1"}, - "activity": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, - "request-changes": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes"}, + "activity": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, + "request-changes": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes" + }, "diff": { - "href": "repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:16182c4698fb%0D1fbd047cd123?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:16182c4698fb%0D1fbd047cd123?from_pullrequest_id=1" }, - "approve": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, - "statuses": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, + "approve": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, + "statuses": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, }, "title": "PRTitle", "close_source_branch": true, @@ -80,13 +82,13 @@ responses[None] = { "hash": "1fbd047cd99a", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/1fbd047cd99a"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, @@ -104,13 +106,13 @@ responses[None] = { "hash": "16182c4123fb", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/16182c4123fb"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/16182c4123fb"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/16182c4123fb"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, diff --git a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments/POST b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments/POST index 930dbea44..56465902e 100644 --- a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments/POST +++ b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments/POST @@ -1,6 +1,8 @@ responses['{"content": {"raw": "hello world"}}'] = { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/comments/196230119"}, + "self": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments/196230119" + }, "html": { "href": "https://bitbucket.org/TestWorkspace1/testrepository1/pull-requests/1/_/diff#comment-196230119" }, @@ -10,7 +12,7 @@ responses['{"content": {"raw": "hello world"}}'] = { "type": "pullrequest", "id": 1, "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/pull-requests/1"}, }, "title": "PRTitle", diff --git a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline/POST b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline/POST index 6aaeda3fd..f7898464d 100644 --- a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline/POST +++ b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline/POST @@ -9,22 +9,24 @@ responses['{"id": 1}'] = { "title": {"raw": "PRTitle", "markup": "markdown", "html": "

PRTitle

", "type": "rendered"}, }, "links": { - "decline": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, + "decline": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, "diffstat": { - "href": "repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:36bb9607a8c9%0D1594fc17b232?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:36bb9607a8c9%0D1594fc17b232?from_pullrequest_id=1" }, - "commits": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, - "self": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1"}, - "comments": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, - "merge": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, + "commits": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1"}, + "comments": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, + "merge": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/pull-requests/1"}, - "activity": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, - "request-changes": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes"}, + "activity": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, + "request-changes": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes" + }, "diff": { - "href": "repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:36bb9607a8c9%0D1594fc17b232?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:36bb9607a8c9%0D1594fc17b232?from_pullrequest_id=1" }, - "approve": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, - "statuses": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, + "approve": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, + "statuses": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, }, "close_source_branch": false, "reviewers": [], @@ -37,13 +39,13 @@ responses['{"id": 1}'] = { "hash": "1594fc17b232", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/1594fc17b232"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1594fc17b232"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/1594fc17b232"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, @@ -60,13 +62,13 @@ responses['{"id": 1}'] = { "hash": "4ed89e8b0a21", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/4ed89e8b0a21"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/4ed89e8b0a21"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/4ed89e8b0a21"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, diff --git a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge/POST b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge/POST index a8a4df2ac..6e95e82bd 100644 --- a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge/POST +++ b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge/POST @@ -9,22 +9,24 @@ responses['{"close_source_branch": true, "merge_strategy": null}'] = { "title": {"raw": "PRTitle", "markup": "markdown", "html": "

PRTitle

", "type": "rendered"}, }, "links": { - "decline": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, + "decline": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, "diffstat": { - "href": "repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:36bb9607a8c9%0D1594fc17b232?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:36bb9607a8c9%0D1594fc17b232?from_pullrequest_id=1" }, - "commits": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, - "self": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1"}, - "comments": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, - "merge": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, + "commits": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1"}, + "comments": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, + "merge": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/pull-requests/1"}, - "activity": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, - "request-changes": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes"}, + "activity": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, + "request-changes": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes" + }, "diff": { - "href": "repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:36bb9607a8c9%0D1594fc17b232?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:36bb9607a8c9%0D1594fc17b232?from_pullrequest_id=1" }, - "approve": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, - "statuses": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, + "approve": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, + "statuses": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, }, "close_source_branch": false, "reviewers": [], @@ -37,13 +39,13 @@ responses['{"close_source_branch": true, "merge_strategy": null}'] = { "hash": "1594fc17b232", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/1594fc17b232"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1594fc17b232"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/1594fc17b232"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, @@ -60,13 +62,13 @@ responses['{"close_source_branch": true, "merge_strategy": null}'] = { "hash": "4ed89e8b0a21", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/4ed89e8b0a21"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/4ed89e8b0a21"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/4ed89e8b0a21"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, @@ -86,7 +88,7 @@ responses['{"close_source_branch": true, "merge_strategy": null}'] = { "hash": "36bb9607a8c9e0c6222342486e3393ae154b46c0", "links": { "self": { - "href": "repositories/TestWorkspace1/testrepository1/commit/36bb9607a8c9e0c6222342486e3393ae154b46c0" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/36bb9607a8c9e0c6222342486e3393ae154b46c0" }, "html": { "href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/36bb9607a8c9e0c6222342486e3393ae154b46c0" diff --git a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/GET b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/GET index 25a227a24..ce184709d 100644 --- a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/GET +++ b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/GET @@ -5,24 +5,38 @@ responses[None] = { { "description": "PRDescription", "links": { - "decline": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, + "decline": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline" + }, "diffstat": { - "href": "repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:16182c4123fb%0D1fbd047cd99a?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:16182c4123fb%0D1fbd047cd99a?from_pullrequest_id=1" + }, + "commits": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/commits" + }, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1"}, + "comments": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments" + }, + "merge": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge" }, - "commits": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, - "self": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1"}, - "comments": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, - "merge": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/pull-requests/123"}, - "activity": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, + "activity": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/activity" + }, "request-changes": { - "href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes" }, "diff": { - "href": "repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:16182c4123fb%0D1fbd047cd99a?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:16182c4123fb%0D1fbd047cd99a?from_pullrequest_id=1" + }, + "approve": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/approve" + }, + "statuses": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses" }, - "approve": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, - "statuses": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, }, "title": "PRTitle", "close_source_branch": true, @@ -33,13 +47,15 @@ responses[None] = { "hash": "1fbd047cd99a", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a"}, + "self": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a" + }, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/1fbd047cd99a"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, @@ -62,13 +78,15 @@ responses[None] = { "hash": "16182c4123fb", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/16182c4123fb"}, + "self": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/16182c4123fb" + }, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/16182c4123fb"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, @@ -104,24 +122,38 @@ responses[None] = { { "description": "PRDescription", "links": { - "decline": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, + "decline": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline" + }, "diffstat": { - "href": "repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:16182c4123fb%0D1fbd047cd99a?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:16182c4123fb%0D1fbd047cd99a?from_pullrequest_id=1" + }, + "commits": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/commits" + }, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1"}, + "comments": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments" + }, + "merge": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge" }, - "commits": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, - "self": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1"}, - "comments": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, - "merge": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/pull-requests/123"}, - "activity": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, + "activity": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/activity" + }, "request-changes": { - "href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes" }, "diff": { - "href": "repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:16182c4123fb%0D1fbd047cd99a?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:16182c4123fb%0D1fbd047cd99a?from_pullrequest_id=1" + }, + "approve": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/approve" + }, + "statuses": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses" }, - "approve": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, - "statuses": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, }, "title": "PRTitle", "close_source_branch": true, @@ -132,13 +164,15 @@ responses[None] = { "hash": "1fbd047cd99a", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a"}, + "self": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a" + }, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/1fbd047cd99a"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, @@ -161,13 +195,15 @@ responses[None] = { "hash": "16182c4123fb", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/16182c4123fb"}, + "self": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/16182c4123fb" + }, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/16182c4123fb"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, diff --git a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/POST b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/POST index 03af9fcd5..7fa0dca6d 100644 --- a/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/POST +++ b/tests/responses/bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/POST @@ -13,22 +13,24 @@ responses[ "type": "pullrequest", "description": "PRDescription", "links": { - "decline": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, + "decline": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/decline"}, "diffstat": { - "href": "repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:16182c4698fb%0D1fbd047cd123?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diffstat/TestWorkspace1/testrepository1:16182c4698fb%0D1fbd047cd123?from_pullrequest_id=1" }, - "commits": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, - "self": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1"}, - "comments": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, - "merge": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, + "commits": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/commits"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1"}, + "comments": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/comments"}, + "merge": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/merge"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/pull-requests/1"}, - "activity": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, - "request-changes": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes"}, + "activity": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/activity"}, + "request-changes": { + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/request-changes" + }, "diff": { - "href": "repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:16182c4698fb%0D1fbd047cd123?from_pullrequest_id=1" + "href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/diff/TestWorkspace1/testrepository1:16182c4698fb%0D1fbd047cd123?from_pullrequest_id=1" }, - "approve": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, - "statuses": {"href": "repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, + "approve": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/approve"}, + "statuses": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/statuses"}, }, "title": "PRTitle", "close_source_branch": true, @@ -82,13 +84,13 @@ responses[ "hash": "1fbd047cd99a", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/1fbd047cd99a"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, @@ -106,13 +108,13 @@ responses[ "hash": "16182c4123fb", "type": "commit", "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1/commit/16182c4123fb"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/16182c4123fb"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/16182c4123fb"}, }, }, "repository": { "links": { - "self": {"href": "repositories/TestWorkspace1/testrepository1"}, + "self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"}, "html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"}, "avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"}, }, diff --git a/tests/responses/bitbucket/server/rest/api/1.0/projects/PRJ/PUT b/tests/responses/bitbucket/server/rest/api/1.0/projects/PRJ/PUT index 7698c757f..58f4b7da5 100644 --- a/tests/responses/bitbucket/server/rest/api/1.0/projects/PRJ/PUT +++ b/tests/responses/bitbucket/server/rest/api/1.0/projects/PRJ/PUT @@ -1,4 +1,4 @@ -responses['{"data": {"name": "New name"}}'] = { +responses['{"name": "New name"}'] = { "key": "PRJ", "id": 1, "name": "New name", @@ -7,7 +7,7 @@ responses['{"data": {"name": "New name"}}'] = { "type": "NORMAL", "links": {"self": [{"href": "bitbucket/server/rest/api/1.0/projects/PRJ"}]}, } -responses['{"data": {"key": "NEWKEY"}}'] = { +responses['{"key": "NEWKEY"}'] = { "key": "NEWKEY", "id": 1, "name": "My Cool Project", @@ -16,7 +16,7 @@ responses['{"data": {"key": "NEWKEY"}}'] = { "type": "NORMAL", "links": {"self": [{"href": "bitbucket/server/rest/api/1.0/projects/NEWKEY"}]}, } -responses['{"data": {"description": "New description."}}'] = { +responses['{"description": "New description."}'] = { "key": "PRJ", "id": 1, "name": "My Cool Project", @@ -25,7 +25,7 @@ responses['{"data": {"description": "New description."}}'] = { "type": "NORMAL", "links": {"self": [{"href": "bitbucket/server/rest/api/1.0/projects/PRJ"}]}, } -responses['{"data": {"public": false}}'] = { +responses['{"public": false}'] = { "key": "PRJ", "id": 1, "name": "My Cool Project", diff --git a/tests/responses/bitbucket/server/rest/api/1.0/projects/PRJ/repos/my-repo1-slug/PUT b/tests/responses/bitbucket/server/rest/api/1.0/projects/PRJ/repos/my-repo1-slug/PUT index b748e29a8..558c6c60d 100644 --- a/tests/responses/bitbucket/server/rest/api/1.0/projects/PRJ/repos/my-repo1-slug/PUT +++ b/tests/responses/bitbucket/server/rest/api/1.0/projects/PRJ/repos/my-repo1-slug/PUT @@ -1,5 +1,5 @@ responses = { - '{"data": {"name": "New name"}}': { + '{"name": "New name"}': { "slug": "my-repo1-slug", "id": 1, "name": "New name", @@ -27,7 +27,7 @@ responses = { "self": [{"href": "bitbucket/server/rest/api/1.0/projects/PRJ/repos/my-repo1"}], }, }, - '{"data": {"description": "New description."}}': { + '{"description": "New description."}': { "slug": "my-repo1-slug", "id": 1, "name": "My repo 1", @@ -55,7 +55,7 @@ responses = { "self": [{"href": "bitbucket/server/rest/api/1.0/projects/PRJ/repos/my-repo1"}], }, }, - '{"data": {"public": false}}': { + '{"public": false}': { "slug": "my-repo1-slug", "id": 1, "name": "My repo 1", @@ -83,7 +83,7 @@ responses = { "self": [{"href": "bitbucket/server/rest/api/1.0/projects/PRJ/repos/my-repo1"}], }, }, - '{"data": {"forkable": false}}': { + '{"forkable": false}': { "slug": "my-repo1-slug", "id": 1, "name": "My repo 1", diff --git a/tests/test_bitbucket_cloud.py b/tests/test_bitbucket_cloud.py index e8c1e5682..a7aaa6fcd 100644 --- a/tests/test_bitbucket_cloud.py +++ b/tests/test_bitbucket_cloud.py @@ -26,23 +26,20 @@ def _datetimetostr(dtime): return dtime.strftime("%Y-%m-%d %H:%M:%S.%f") +@pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") class TestBasic: - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_repositories(self): result = [x["name"] for x in BITBUCKET.get_repositories("TestWorkspace1")] assert result == ["testrepository1", "testrepository2"], "Result of [get_repositories(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_pipelines(self): result = [x["uuid"] for x in BITBUCKET.get_pipelines("TestWorkspace1", "testrepository1")] assert result == ["{PipelineUuid}"], "Result of [get_pipelines(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_trigger_pipeline(self): result = BITBUCKET.trigger_pipeline("TestWorkspace1", "testrepository1") assert result["uuid"] == "{PipelineUuid}", "Result of [trigger_pipeline(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_pipeline(self): result = BITBUCKET.get_pipeline("TestWorkspace1", "testrepository1", "{PipelineUuid}") assert result["state"]["name"] == "COMPLETED", "Result of [get_pipeline(...)]" @@ -52,67 +49,56 @@ def test_get_pipeline(self): assert result.get_data("state")["name"] == "COMPLETED", "Pipeline state" assert result.completed_on is None, "Pipeline completed time" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_stop_pipeline(self): result = BITBUCKET.stop_pipeline("TestWorkspace1", "testrepository1", "{PipelineUuid}") assert result == {}, "Result of [stop_pipeline(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_pipeline_steps(self): result = [ x["uuid"] for x in BITBUCKET.get_pipeline_steps("TestWorkspace1", "testrepository1", "{PipelineUuid}") ] assert result == ["{PipelineStep1Uuid}", "{PipelineStep2Uuid}"], "Result of [get_pipeline_steps(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_pipeline_step(self): result = BITBUCKET.get_pipeline_step( "TestWorkspace1", "testrepository1", "{PipelineUuid}", "{PipelineStep1Uuid}" ) assert result["uuid"] == "{PipelineStep1Uuid}", "Result of [get_pipeline_step(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_pipeline_step_log_1(self): result = BITBUCKET.get_pipeline_step_log( "TestWorkspace1", "testrepository1", "{PipelineUuid}", "{PipelineStep1Uuid}" ) assert result is None, "Result of step1 [get_pipeline_step_log(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_pipeline_step_log_2(self): result = BITBUCKET.get_pipeline_step_log( "TestWorkspace1", "testrepository1", "{PipelineUuid}", "{PipelineStep2Uuid}" ) assert result == b"Log content", "Result of step2 [get_pipeline_step_log(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_issues(self): result = [x["title"] for x in BITBUCKET.get_issues("TestWorkspace1", "testrepository1")] assert result == ["First issue", "Second issue"], "Result of [get_issues(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_create_issue(self): result = BITBUCKET.create_issue("TestWorkspace1", "testrepository1", "Title", "Description", "bug", "minor")[ "content" ]["raw"] assert result == "Description", "Result of [create_issue(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_issue(self): result = BITBUCKET.get_issue("TestWorkspace1", "testrepository1", 3)["kind"] assert result == "bug", "Result of [get_issue(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_update_issue(self): result = BITBUCKET.update_issue("TestWorkspace1", "testrepository1", 3, kind="enhancement")["kind"] assert result == "enhancement", "Result of [update_issue(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_delete_issue(self): result = BITBUCKET.delete_issue("TestWorkspace1", "testrepository1", 3)["title"] assert result == "Title deleted issue", "Result of [get_issue(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_branch_restrictions(self): result = [x["kind"] for x in BITBUCKET.get_branch_restrictions("TestWorkspace1", "testrepository1")] assert result == [ @@ -123,39 +109,31 @@ def test_get_branch_restrictions(self): "push", ], "Result of [get_branch_restrictions(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_update_branch_restriction(self): result = BITBUCKET.update_branch_restriction("TestWorkspace1", "testrepository1", 17203842, branch="master")[ "pattern" ] assert result == "master", "Result of [update_branch_restrictions(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_delete_branch_restriction(self): result = BITBUCKET.delete_branch_restriction("TestWorkspace1", "testrepository1", 17203842)["pattern"] assert result == "deleted_branch", "Result of [update_branch_restrictions(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_default_reviewers(self): result = [x["display_name"] for x in BITBUCKET.get_default_reviewers("TestWorkspace1", "testrepository1")] assert result == ["DefaultReviewer1"], "Result of [get_default_reviewers(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_is_default_reviewer(self): result = BITBUCKET.is_default_reviewer("TestWorkspace1", "testrepository1", "DefaultReviewerNo") assert result is False, "Result of [is_default_reviewer(...)]" result = BITBUCKET.is_default_reviewer("TestWorkspace1", "testrepository1", "DefaultReviewer1") assert result is True, "Result of [is_default_reviewer(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_delete_default_reviewer(self): - result = ( - CLOUD.workspaces.get("TestWorkspace1") - .repositories.get("testrepository1") - .default_reviewers.get("DefaultReviewer1") - .delete() - ) - assert result + result = BITBUCKET.delete_default_reviewer("TestWorkspace1", "testrepository1", "DefaultReviewer1")[ + "account_id" + ] + assert result == "DefaultReviewer1AccountId", "Result of [delete_default_reviewer(...)]" @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") diff --git a/tests/test_bitbucket_server.py b/tests/test_bitbucket_server.py index 9d6ba360b..9bc37c6fb 100644 --- a/tests/test_bitbucket_server.py +++ b/tests/test_bitbucket_server.py @@ -15,8 +15,8 @@ pass +@pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") class TestBasic: - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_global_permissions(self): result = list(BITBUCKET.groups.each()) assert [x.name for x in result] == ["group_a", "group_b", "group_c", "group_d"], "Each global group" @@ -36,7 +36,7 @@ def test_global_permissions(self): group = BITBUCKET.groups.get("group_a") assert group.name == "group_a", "Get a group" - assert group.delete() is True, "Delete a group" + assert group.delete() == {}, "Delete a group" result = list(BITBUCKET.users.each()) assert [x.permission for x in result] == [ @@ -76,9 +76,8 @@ def test_global_permissions(self): user = BITBUCKET.users.get("jcitizen1") assert user.name == "jcitizen1", "Get a user" - assert user.delete() is True, "Delete a user" + assert user.delete() == {}, "Delete a user" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_projects(self): result = list(BITBUCKET.projects.each()) assert [x.key for x in result] == ["PRJ", "PRJ1"], "Each project keys" @@ -92,6 +91,8 @@ def test_projects(self): project = BITBUCKET.projects.get("My Cool Project", by="name") assert project.key == "PRJ", "Get project by name" + assert project.id == 1, "The project id" + assert project.type == "NORMAL", "The project type" assert project.name == "My Cool Project", "The project name" project.name = "New name" assert project.name == "New name", "Update the project name" @@ -108,7 +109,6 @@ def test_projects(self): project.key = "NEWKEY" assert project.key == "NEWKEY", "Update the project key" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_project_permissions(self): project = BITBUCKET.projects.get("PRJ") @@ -125,7 +125,7 @@ def test_project_permissions(self): group = project.groups.get("group_a") assert group.name == "group_a", "Get a group" - assert group.delete() is True, "Delete a group" + assert group.delete() == {}, "Delete a group" result = list(project.users.each()) assert [x.permission for x in result] == ["ADMIN", "WRITE", "READ"], "Each permission of project user" @@ -157,9 +157,8 @@ def test_project_permissions(self): user = project.users.get("jcitizen1") assert user.name == "jcitizen1", "Get a user" - assert user.delete() is True, "Delete a user" + assert user.delete() == {}, "Delete a user" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_repositories(self): project = BITBUCKET.projects.get("PRJ") result = list(project.repos.each()) @@ -220,7 +219,6 @@ def test_repositories(self): assert [x.id for x in repo.related()] == [2], "The related repositories" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_repository_permissions(self): repo = BITBUCKET.projects.get("PRJ").repos.get("my-repo1-slug") @@ -237,7 +235,7 @@ def test_repository_permissions(self): group = repo.groups.get("group_a") assert group.name == "group_a", "Get a group" - assert group.delete() is True, "Delete a group" + assert group.delete() == {}, "Delete a group" result = list(repo.users.each()) assert [x.permission for x in result] == ["ADMIN", "WRITE", "READ"], "Each permission of repo user" @@ -265,4 +263,4 @@ def test_repository_permissions(self): user = repo.users.get("jcitizen1") assert user.name == "jcitizen1", "Get a user" - assert user.delete() is True, "Delete a user" + assert user.delete() == {}, "Delete a user" diff --git a/tests/test_servicedesk.py b/tests/test_servicedesk.py index 7f3b66064..274e45c6f 100644 --- a/tests/test_servicedesk.py +++ b/tests/test_servicedesk.py @@ -15,18 +15,16 @@ pass +@pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") class TestBasic: - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_create_customer(self): result = SERVICEDESK.create_customer("{displayName}", "{emailAddress}")["emailAddress"] assert result == "{emailAddress}", "Result of [create_customer()]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_organisations(self): result = [x["name"] for x in SERVICEDESK.get_organisations()["values"]] assert result == ["Charlie Cakes Franchises"], "Result of [get_organisations()]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_organisations_servicedesk_id(self): result = [x["name"] for x in SERVICEDESK.get_organisations(service_desk_id="{serviceDeskId}")["values"]] assert result == [ @@ -35,61 +33,50 @@ def test_get_organisations_servicedesk_id(self): "The Adjustment Bureau", ], "Result of [get_organisations(service_desk_id)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_organization(self): result = SERVICEDESK.get_organization("{organizationId}") assert result["name"] == "Charlie Cakes Franchises", "Result of [test_get_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_users_in_organization(self): result = [x["emailAddress"] for x in SERVICEDESK.get_users_in_organization("{organizationId}")["values"]] assert result == ["fred@example.com", "bob@example.com"], "Result of [get_users_in_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_create_organization(self): result = SERVICEDESK.create_organization("Charlie Chocolate Franchises") assert result["id"] == "2", "Result of [create_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_add_organization(self): result = SERVICEDESK.add_organization("{serviceDeskId}", "{organizationId}") assert result is None, "Result of [add_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_remove_organization(self): result = SERVICEDESK.remove_organization("{serviceDeskId}", "{organizationId}") assert result is None, "Result of [remove_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_delete_organization(self): result = SERVICEDESK.delete_organization("{organizationId}") assert result is None, "Result of [delete_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_add_users_to_organization(self): result = SERVICEDESK.add_users_to_organization( "{organizationId}", account_list=["{accountId1}", "{accountId2}"] ) assert result is None, "Result of [add_users_to_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_remove_users_from_organization(self): result = SERVICEDESK.remove_users_from_organization( "{organizationId}", account_list=["{accountId1}", "{accountId2}"] ) assert result is None, "Result of [remove_users_from_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_get_customers(self): result = [x["emailAddress"] for x in SERVICEDESK.get_customers("{serviceDeskId}", query=None)["values"]] assert result == ["fred@example.com"], "Result of [remove_users_from_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_add_customers(self): result = SERVICEDESK.add_customers("{serviceDeskId}", list_of_accountids=["{accountId1}", "{accountId2}"]) assert result is None, "Result of [remove_users_from_organization(...)]" - @pytest.mark.skipif(sys.version_info < (3, 4), reason="requires python3.4") def test_remove_customers(self): result = SERVICEDESK.remove_customers("{serviceDeskId}", list_of_accountids=["{accountId1}", "{accountId2}"]) assert result is None, "Result of [remove_users_from_organization(...)]" diff --git a/tox.ini b/tox.ini index 9705cf33c..ec52354af 100644 --- a/tox.ini +++ b/tox.ini @@ -13,8 +13,7 @@ deps = commands = coverage erase pytest -v --cov=atlassian --cov-branch - coverage report --omit='.tox/*' - coverage html --omit='.tox/*' + coverage html extras = kerberos parallel_show_output = true