]>
Commit | Line | Data |
---|---|---|
26ccb61a MT |
1 | #!/usr/bin/python3 |
2 | ||
3 | import json | |
4 | import urllib.parse | |
5 | ||
d0502fb3 | 6 | from . import httpclient |
26ccb61a MT |
7 | from . import misc |
8 | from .decorators import * | |
9 | ||
10 | class BugzillaError(Exception): | |
11 | pass | |
12 | ||
13 | class Bugzilla(misc.Object): | |
14 | def init(self, api_key=None): | |
15 | if api_key is None: | |
16 | api_key = self.settings.get("bugzilla-api-key") | |
17 | ||
18 | # Store the API key | |
19 | self.api_key = api_key | |
20 | ||
21 | @property | |
22 | def url(self): | |
23 | """ | |
24 | Returns the base URL of a Bugzilla instance | |
25 | """ | |
26 | return self.settings.get("bugzilla-url") | |
27 | ||
28 | def make_url(self, *args, **kwargs): | |
29 | """ | |
30 | Composes a URL based on the base URL | |
31 | """ | |
32 | url = urllib.parse.urljoin(self.url, *args) | |
33 | ||
34 | # Append any query arguments | |
35 | if kwargs: | |
36 | url = "%s?%s" % (url, urllib.parse.urlencode(kwargs)) | |
37 | ||
38 | return url | |
39 | ||
40 | async def _request(self, method, url, data=None): | |
41 | if data is None: | |
42 | data = {} | |
43 | ||
44 | # Headers | |
45 | headers = { | |
46 | # Authenticate all requests | |
47 | "X-BUGZILLA-API-KEY" : self.api_key, | |
48 | } | |
49 | ||
50 | # Make the URL | |
51 | url = self.make_url(url) | |
52 | ||
53 | # Fallback authentication because some API endpoints | |
54 | # do not accept the API key in the header | |
55 | data |= { "api_key" : self.api_key } | |
56 | ||
57 | # Encode body | |
58 | body = None | |
59 | ||
60 | # For GET requests, append query arguments | |
61 | if method == "GET": | |
62 | if data: | |
63 | url = "%s?%s" % (url, urllib.parse.urlencode(data)) | |
64 | ||
65 | # For POST/PUT encode all arguments as JSON | |
66 | elif method in ("POST", "PUT"): | |
67 | headers |= { | |
68 | "Content-Type" : "application/json", | |
69 | } | |
70 | ||
71 | body = json.dumps(data) | |
72 | ||
73 | # Send the request and wait for a response | |
74 | res = await self.backend.http_client.fetch( | |
75 | url, method=method, headers=headers, body=body) | |
76 | ||
77 | # Decode JSON response | |
78 | body = json.loads(res.body) | |
79 | ||
80 | # Check for any errors | |
81 | if "error" in body: | |
82 | # Fetch code and message | |
83 | code, message = body.get("code"), body.get("message") | |
84 | ||
85 | # Handle any so far unhandled errors | |
86 | raise BugzillaError(message) | |
87 | ||
88 | # Return an empty response | |
89 | return body | |
90 | ||
91 | async def get_user(self, uid): | |
92 | """ | |
93 | Fetches a user from Bugzilla | |
94 | """ | |
d0502fb3 MT |
95 | try: |
96 | response = await self._request("GET", "/rest/user/%s" % uid) | |
97 | ||
98 | # Return nothing if the user could not be found | |
99 | except httpclient.HTTPError as e: | |
100 | if e.code == 404: | |
101 | return | |
102 | ||
103 | raise e | |
26ccb61a MT |
104 | |
105 | # Return the user object | |
106 | for data in response.get("users"): | |
107 | return User(self.backend, data) | |
108 | ||
109 | ||
110 | ||
111 | class User(misc.Object): | |
112 | def init(self, data): | |
113 | self.data = data | |
114 | ||
115 | @property | |
116 | def id(self): | |
117 | return self.data.get("id") | |
118 | ||
119 | async def _update(self, **kwargs): | |
120 | # Send the request | |
121 | await self.backend.bugzilla._request("PUT", "/rest/user/%s" % self.id, **kwargs) | |
122 | ||
123 | # XXX apply changes to the User object? | |
124 | ||
125 | async def disable(self, text=None): | |
126 | """ | |
127 | Disables this user | |
128 | """ | |
129 | if not text: | |
130 | text = "DISABLED" | |
131 | ||
132 | # Update the user | |
133 | await self._update(data={ | |
134 | "login_denied_text" : text, | |
135 | }) |