diff --git a/atlassian/__init__.py b/atlassian/__init__.py index c6dd219e4..00a40ad47 100644 --- a/atlassian/__init__.py +++ b/atlassian/__init__.py @@ -1,7 +1,9 @@ import json import logging +from urllib.parse import urlencode, urljoin import requests + log = logging.getLogger("atlassian") @@ -22,12 +24,23 @@ def log_curl_debug(self, method, path, data=None, headers={}, level=logging.DEBU url='{0}{1}'.format(self.url, path)) log.log(level=level, msg=message) - def request(self, method='GET', path='/', data=None, + def resource_url(self, resource, version='latest'): + return '/'.join(['rest', 'api', version, resource]) + + def request(self, method='GET', path='/', data=None, flags=None, params=None, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}): self.log_curl_debug(method=method, path=path, headers=headers, data=data) + url = urljoin(self.url, path) + if params or flags: + url += '?' + if params: + url += urlencode(params or {}) + if flags: + url += ('&' if params else '') + '&'.join(flags or []) + response = requests.request( method=method, - url='{0}{1}'.format(self.url, path), + url=url, headers=headers, data=json.dumps(data), auth=(self.username, self.password), @@ -42,8 +55,8 @@ def request(self, method='GET', path='/', data=None, response.raise_for_status() return response - def get(self, path, data=None, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}): - return self.request('GET', path=path, data=data, headers=headers).json() + def get(self, path, data=None, flags=None, params=None, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}): + return self.request('GET', path=path, flags=flags, params=params, data=data, headers=headers).json() def post(self, path, data=None, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}): try: @@ -60,12 +73,18 @@ def put(self, path, data=None, headers={'Content-Type': 'application/json', 'Acc return None def delete(self, path, data=None, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}): - return self.request('DELETE', path=path, data=data, headers=headers).json() + """ + Deletes resources at given paths. + :rtype: dict + :return: Empty dictionary to have consistent interface. Some of Atlassian rest resources don't return any content. + """ + self.request('DELETE', path=path, data=data, headers=headers) from .confluence import Confluence from .jira import Jira from .stash import Stash from .portfolio import Portfolio +from .bamboo import Bamboo -__all__ = ['Confluence', 'Jira', 'Stash', 'Portfolio'] +__all__ = ['Confluence', 'Jira', 'Stash', 'Portfolio', 'Bamboo'] diff --git a/atlassian/bamboo.py b/atlassian/bamboo.py new file mode 100644 index 000000000..7c08667ff --- /dev/null +++ b/atlassian/bamboo.py @@ -0,0 +1,108 @@ +import logging +from atlassian import AtlassianRestAPI + +log = logging.getLogger('atlassian.stash') + + +class Bamboo(AtlassianRestAPI): + + def base_list_call(self, resource, expand, favourite, cloverEnabled, start_index, max_results, **kwargs): + flags = [] + params = {'start-index': start_index, 'max-results': max_results} + if expand: + params['expand'] = expand + if favourite: + flags.append('favourite') + if cloverEnabled: + flags.append('cloverEnabled') + params.update(kwargs) + return self.get(self.resource_url(resource), flags=flags, params=params) + + def projects(self, expand=None, favourite=False, cloverEnabled=False, start_index=0, max_results=25): + return self.base_list_call('project', expand, favourite, cloverEnabled, start_index, max_results) + + def plans(self, expand=None, favourite=False, cloverEnabled=False, start_index=0, max_results=25): + return self.base_list_call("plan", expand, favourite, cloverEnabled, start_index, max_results) + + def results(self, project_key=None, plan_key=None, build_number=None, expand=None, favourite=False, + cloverEnabled=False, label=None, issueKey=None, start_index=0, max_results=25): + resource = "result" + if project_key and plan_key and build_number: + resource += "/{}-{}/{}".format(project_key, plan_key, build_number) + elif project_key and plan_key: + resource += "/{}-{}".format(project_key, plan_key) + elif project_key: + resource += '/' + project_key + + params = {} + if issueKey: + params['issueKey'] = issueKey + return self.base_list_call(resource, expand, favourite, cloverEnabled, start_index, max_results, **params) + + def latest_results(self, expand=None, favourite=False, cloverEnabled=False, label=None, issueKey=None, + start_index=0, max_results=25): + return self.results(expand=expand, favourite=favourite, cloverEnabled=cloverEnabled, + label=label, issueKey=issueKey, start_index=start_index, max_results=max_results) + + def project_latest_results(self, project_key, expand=None, favourite=False, cloverEnabled=False, label=None, issueKey=None, + start_index=0, max_results=25): + return self.results(project_key, expand=expand, favourite=favourite, cloverEnabled=cloverEnabled, + label=label, issueKey=issueKey, start_index=start_index, max_results=max_results) + + def plan_results(self, project_key, plan_key, expand=None, favourite=False, cloverEnabled=False, label=None, issueKey=None, + start_index=0, max_results=25): + return self.results(project_key, plan_key, expand=expand, favourite=favourite, cloverEnabled=cloverEnabled, + label=label, issueKey=issueKey, start_index=start_index, max_results=max_results) + + def build_result(self, project_key, plan_key, build_key, expand=None, favourite=False, cloverEnabled=False, label=None, issueKey=None, + start_index=0, max_results=25): + return self.results(project_key, plan_key, expand=expand, favourite=favourite, cloverEnabled=cloverEnabled, + label=label, issueKey=issueKey, start_index=start_index, max_results=max_results) + + def reports(self, expand=None, start_index=0, max_results=25): + params = {'start-index': start_index, 'max-results': max_results} + if expand: + params['expand'] = expand + + return self.get(self.resource_url('chart/reports'), params=params) + + def chart(self, reportKey, buildKeys, groupByPeriod, dateFilter=None, dateFrom=None, dateTo=None, + width=None, height=None, start_index=9, max_results=25): + params = {'reportKey': reportKey, 'buildKeys': buildKeys, 'groupByPeriod': groupByPeriod, + 'start-index': start_index, 'max-results': max_results} + if dateFilter: + params['dateFilter'] = dateFilter + if dateFilter == 'RANGE': + params['dateFrom'] = dateFrom + params['dateTo'] = dateTo + if width: + params['width'] = width + if height: + params['height'] = height + return self.get(self.resource_url('chart'), params=params) + + def comments(self, project_key, plan_key, build_number, start_index=0, max_results=25): + resource = "result/{}-{}-{}/comment".format(project_key, plan_key, build_number) + params = {'start-index': start_index, 'max-results': max_results} + return self.get(self.resource_url(resource), params=params) + + def create_comment(self, project_key, plan_key, build_number, comment, author=None): + resource = "result/{}-{}-{}/comment".format(project_key, plan_key, build_number) + comment_data = {'author': author if author else self.username, 'content': comment} + return self.post(self.resource_url(resource), data=comment_data) + + def labels(self, project_key, plan_key, build_number, start_index=0, max_results=25): + resource = "result/{}-{}-{}/label".format(project_key, plan_key, build_number) + params = {'start-index': start_index, 'max-results': max_results} + return self.get(self.resource_url(resource), params=params) + + def create_label(self, project_key, plan_key, build_number, label): + resource = "result/{}-{}-{}/label".format(project_key, plan_key, build_number) + return self.post(self.resource_url(resource), data={'name': label}) + + def delete_label(self, project_key, plan_key, build_number, label): + resource = "result/{}-{}-{}/label/{}".format(project_key, plan_key, build_number, label) + return self.delete(self.resource_url(resource)) + + def server_info(self): + return self.get(self.resource_url('info'))