Skip to content

Commit 9564ffe

Browse files
committed
PYAPI-22 Stash Project Administrators
1 parent 3258e48 commit 9564ffe

12 files changed

+206
-34
lines changed

PKG-INFO

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Metadata-Version: 1.1
22
Name: atlassian-python-api
3-
Version: 0.3.0
3+
Version: 0.6.3
44
Author: Matt Harasymczuk
55
Author-email: code at mattagile com
66
Maintainer: Matt Harasymczuk

atlassian/__init__.py

+43-17
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,56 @@
11
import json
2+
import logging
23
import requests
34

45

5-
class Atlassian:
6+
logging.basicConfig(level=logging.INFO, format="[%(asctime).19s] [%(levelname)s] %(message)s")
7+
logging.getLogger("requests").setLevel(logging.WARNING)
8+
log = logging.getLogger("atlassian")
9+
10+
11+
class AtlassianRestAPI:
612

713
def __init__(self, url, username, password):
814
self.url = url
915
self.username = username
1016
self.password = password
1117

12-
def get(self, path, headers={"Content-Type": "application/json", "Accept": "application/json"}):
13-
url = "{0}{1}".format(self.url, path)
14-
return requests.get(url, headers=headers, auth=(self.username, self.password), timeout=60)
15-
16-
def post(self, path, data=None, headers={"Content-Type": "application/json", "Accept": "application/json"}):
17-
url = "{0}{1}".format(self.url, path)
18-
return requests.post(url, json.dumps(data), headers=headers, auth=(self.username, self.password))
19-
20-
def put(self, path, data=None, headers={"Content-Type": "application/json", "Accept": "application/json"}):
21-
url = "{0}{1}".format(self.url, path)
22-
return requests.put(url, json.dumps(data), headers=headers, auth=(self.username, self.password))
23-
24-
def delete(self, path, headers={"Content-Type": "application/json", "Accept": "application/json"}):
25-
url = "{0}{1}".format(self.url, path)
26-
return requests.delete(url, headers=headers, auth=(self.username, self.password))
18+
def log_curl_debug(self, method, path, headers={}, data=None):
19+
command = "curl --silent -X {method} -u '{username}':'{password}' -H {headers} {data} '{url}'".format(
20+
method=method,
21+
username=self.username,
22+
password=self.password,
23+
headers=' -H '.join(["'{0}: {1}'".format(key, value) for key, value in headers.items()]),
24+
data='' if not data else "--data '{0}'".format(json.dumps(data)),
25+
url='{0}{1}'.format(self.url, path))
26+
log.debug(command)
27+
28+
def request(self, method='GET', path='/', headers={'Content-Type': 'application/json', 'Accept': 'application/json'}, data=None):
29+
self.log_curl_debug(method, path, headers, data)
30+
response = requests.request(
31+
method=method,
32+
url='{0}{1}'.format(self.url, path),
33+
headers=headers,
34+
data=json.dumps(data),
35+
auth=(self.username, self.password),
36+
timeout=60)
37+
response.raise_for_status()
38+
log.debug('Received: {0}'.format(response.json()))
39+
return response
40+
41+
def get(self, path, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}):
42+
return self.request('GET', path, headers).json()
43+
44+
def post(self, path, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}, data=None):
45+
return self.request('POST', path, headers, data).json()
46+
47+
def put(self, path, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}, data=None):
48+
return self.request('PUT', path, headers, data).json()
49+
50+
def delete(self, path, headers={'Content-Type': 'application/json', 'Accept': 'application/json'}):
51+
return self.request('DELETE', path, headers).json()
2752

2853

2954
from .confluence import Confluence
30-
from .jira import Jira
55+
from .jira import Jira
56+
from .stash import Stash

atlassian/confluence.py

+25-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,35 @@
1-
from atlassian import Atlassian
1+
import logging
2+
from atlassian import AtlassianRestAPI
23

34

4-
class Confluence(Atlassian):
5+
logging.basicConfig(level=logging.INFO, format="[%(asctime).19s] [%(levelname)s] %(message)s")
6+
logging.getLogger("requests").setLevel(logging.WARNING)
7+
log = logging.getLogger("atlassian.confluence")
8+
9+
10+
class Confluence(AtlassianRestAPI):
511

612
def create_page(self, space, parent_id, title, body):
7-
return self.post("/rest/api/content/", {
13+
return self.post("/rest/api/content/", data={
814
"type": "page",
9-
"title": title,
1015
"ancestors": [{"type": "page", "id": parent_id}],
16+
"title": title,
1117
"space": {"key": space},
1218
"body": {"storage": {
1319
"value": body,
1420
"representation": "storage"}}})
21+
22+
def history(self, page_id):
23+
return self.get("/rest/api/content/{0}/history".format(page_id))
24+
25+
def update_page(self, parent_id, page_id, title, body, type="page",):
26+
version = self.history(page_id)["lastUpdated"]["number"] + 1
27+
return self.put("/rest/api/content/{0}".format(page_id), data={
28+
"id": page_id,
29+
"type": type,
30+
"ancestors": [{"type": "page", "id": parent_id}],
31+
"title": title,
32+
"body": {"storage": {
33+
"value": body,
34+
"representation": "storage"}},
35+
"version": {"number": version}})

atlassian/jira.py

+14-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
from atlassian import Atlassian
1+
import logging
2+
from atlassian import AtlassianRestAPI
23

34

4-
class Jira(Atlassian):
5+
logging.basicConfig(level=logging.INFO, format="[%(asctime).19s] [%(levelname)s] %(message)s")
6+
logging.getLogger("requests").setLevel(logging.WARNING)
7+
log = logging.getLogger("atlassian.jira")
8+
9+
10+
class Jira(AtlassianRestAPI):
511

612
def reindex_status(self):
713
return self.get("/rest/api/2/reindex")
@@ -24,11 +30,14 @@ def project(self, key):
2430
def issue(self, key):
2531
return self.get("/rest/api/2/issue/{0}".format(key))
2632

33+
def update_issue_field(self, key, fields):
34+
return self.put("/rest/api/2/issue/{0}".format(key), data={"fields": fields})
35+
2736
def project_leaders(self):
28-
for project in self.projects().json():
37+
for project in self.projects():
2938
key = project["key"]
30-
project_data = self.project(key).json()
31-
lead = self.user(project_data["lead"]["key"]).json()
39+
project_data = self.project(key)
40+
lead = self.user(project_data["lead"]["key"])
3241
yield {
3342
"project_key": key,
3443
"project_name": project["name"],

atlassian/stash.py

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import logging
2+
from atlassian import AtlassianRestAPI
3+
4+
5+
logging.basicConfig(level=logging.INFO, format="[%(asctime).19s] [%(levelname)s] %(message)s")
6+
logging.getLogger("requests").setLevel(logging.WARNING)
7+
log = logging.getLogger("atlassian.stash")
8+
9+
10+
class Stash(AtlassianRestAPI):
11+
12+
def project_list(self):
13+
return self.get('/rest/api/1.0/projects')['values']
14+
15+
def project(self, key):
16+
return self.get('/rest/api/1.0/projects/{}'.format(key))['values']
17+
18+
def project_users(self, key):
19+
return self.get('/rest/api/1.0/projects/{}/permissions/users?limit=99999'.format(key))['values']
20+
21+
def project_users_with_administrator_permissions(self, key):
22+
project_administrators = [user['user'] for user in self.project_users(key) if user['permission'] == 'PROJECT_ADMIN']
23+
24+
for group in self.project_groups_with_administrator_permissions(key):
25+
for user in self.group_members(group):
26+
project_administrators.append(user)
27+
28+
return project_administrators
29+
30+
def project_groups(self, key):
31+
return self.get('/rest/api/1.0/projects/{}/permissions/groups?limit=99999'.format(key))['values']
32+
33+
def project_groups_with_administrator_permissions(self, key):
34+
return [group['group']['name'] for group in self.project_groups(key) if group['permission'] == 'PROJECT_ADMIN']
35+
36+
def project_summary(self, key):
37+
return {
38+
'key': key,
39+
'data': self.project(key),
40+
'users': self.project_users(key),
41+
'groups': self.project_groups(key)}
42+
43+
def group_members(self, group):
44+
return self.get('/rest/api/1.0/admin/groups/more-members?context={}&limit=99999'.format(group))['values']
45+
46+
def all_project_administrators(self):
47+
for project in self.project_list():
48+
log.info("Processing project: {0} - {1}".format(project['key'], project['name']))
49+
yield {
50+
'project_key': project['key'],
51+
'project_name': project['name'],
52+
'project_administrators': [{'email': x['emailAddress'], 'name': x['displayName']} for x in self.project_users_with_administrator_permissions(project['key'])]}

examples/confluence-create-page.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
username="admin",
88
password="admin")
99

10-
status = confluence.create_page(space="~admin", title="This is the title", body="This is the body")
10+
status = confluence.create_page(
11+
space="DEMO",
12+
title="This is the title",
13+
body="This is the body")
1114

12-
pprint(status.json())
15+
pprint(status)

examples/confluence-update-page.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from pprint import pprint
2+
from atlassian import Confluence
3+
4+
5+
confluence = Confluence(
6+
url="http://localhost:8090",
7+
username="admin",
8+
password="admin")
9+
10+
status = confluence.update_page(
11+
page_id=123456,
12+
title="This is the new title",
13+
body="This is the new body")
14+
15+
pprint(status)

examples/jira-jql-query.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
username="admin",
1010
password="admin")
1111

12-
data = jira.jql(JQL).json()
12+
data = jira.jql(JQL)
1313
pprint(data)

examples/jira-sprint-rename.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
start_date="2014-10-13 11:44",
1414
end_date="2014-10-20 09:34")
1515

16-
pprint(data.json())
16+
pprint(data)

examples/stash-project.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from pprint import pprint
2+
from atlassian import Stash
3+
4+
5+
stash = Stash(
6+
url='http://localhost:7990',
7+
username='admin',
8+
password='admin')
9+
10+
data = stash.project_users("DEMO")
11+
12+
13+
def html(project):
14+
html = '<tr><td>{project_key}</td><td>{project_name}</td><td><ul>'.format(**project)
15+
for user in project['project_administrators']:
16+
html += '\n\t<li><a href="mailto:{email}">{name}</a></li>'.format(**user)
17+
return html + '</ul></td></tr>\n'
18+
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import logging
2+
from atlassian import Stash
3+
4+
5+
logging.basicConfig(level=logging.DEBUG, format='[%(asctime).19s] [%(levelname)s] %(message)s')
6+
logging.getLogger('requests').setLevel(logging.WARNING)
7+
log = logging.getLogger('stash-projects-administrators')
8+
9+
stash = Stash(
10+
url='http://localhost:7990',
11+
username='admin',
12+
password='admin')
13+
14+
html = '<table><tr><th>Project Key</th><th>Project Name</th><th>Administrator</th></tr>'
15+
16+
for data in stash.all_project_administrators():
17+
html += '<tr><td>{project_key}</td><td>{project_name}</td><td><ul>'.format(**data)
18+
for user in data['project_administrators']:
19+
html += '<li><a href="mailto:{email}">{name}</a></li>'.format(**user)
20+
html += '</ul></td></tr>'
21+
22+
html += '</table><p></p><p></p>'
23+
24+
print(html)

setup.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
from setuptools import setup
33

44

5-
assert sys.version_info >= (2, 7), "Python 2.7+ required."
5+
assert sys.version_info >= (3, 0), "Python 3.0+ required."
66

77
setup(
88
name="atlassian-python-api",
99
description="Atlassian Python API",
1010
license="Apache License 2.0",
11-
version="0.3.3",
11+
version="0.6.3",
1212
download_url="https://github.com/MattAgile/atlassian-python-api",
1313

1414
author="Matt Harasymczuk",
@@ -21,7 +21,7 @@
2121
include_package_data=True,
2222

2323
zip_safe=False,
24-
install_requires=["requests==2.4.3"],
24+
install_requires=["requests==2.5.3"],
2525

2626
classifiers=[
2727
"Development Status :: 4 - Beta",
@@ -36,6 +36,10 @@
3636
"Operating System :: Microsoft :: Windows",
3737
"Programming Language :: Python",
3838
"Programming Language :: Python :: 3",
39+
"Programming Language :: Python :: 3.0",
40+
"Programming Language :: Python :: 3.1",
41+
"Programming Language :: Python :: 3.2",
42+
"Programming Language :: Python :: 3.3",
3943
"Programming Language :: Python :: 3.4",
4044
"Topic :: Internet",
4145
"Topic :: Internet :: WWW/HTTP",

0 commit comments

Comments
 (0)