Merge ~jsseidel/maas:jsseidel-notifications-api-annotations into maas:master
- Git
- lp:~jsseidel/maas
- jsseidel-notifications-api-annotations
- Merge into master
Proposed by
Spencer Seidel
Status: | Merged |
---|---|
Approved by: | Spencer Seidel |
Approved revision: | b949cc38bfe0e4724d17e0be0f57901b4d18d086 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~jsseidel/maas:jsseidel-notifications-api-annotations |
Merge into: | maas:master |
Diff against target: |
345 lines (+260/-28) 2 files modified
src/maasserver/api/examples/notifications.json (+132/-0) src/maasserver/api/notification.py (+128/-28) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Lander | Approve | ||
Mike Pontillo (community) | Approve | ||
Review via email: mp+361144@code.launchpad.net |
Commit message
Added API annotations and examples to notifications.
Description of the change
To post a comment you must log in.
Revision history for this message
Spencer Seidel (jsseidel) : | # |
Revision history for this message
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b jsseidel-
STATUS: SUCCESS
COMMIT: b949cc38bfe0e47
review:
Approve
Revision history for this message
Spencer Seidel (jsseidel) : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/api/examples/notifications.json b/src/maasserver/api/examples/notifications.json |
2 | new file mode 100644 |
3 | index 0000000..1010eb3 |
4 | --- /dev/null |
5 | +++ b/src/maasserver/api/examples/notifications.json |
6 | @@ -0,0 +1,132 @@ |
7 | +{ |
8 | + "notifications-read": [ |
9 | + { |
10 | + "ident": "dhcp_disabled_all_vlans", |
11 | + "user": null, |
12 | + "users": false, |
13 | + "admins": true, |
14 | + "message": "DHCP is not enabled on any VLAN. This will prevent machines from being able to PXE boot, unless an external DHCP server is being used.", |
15 | + "context": {}, |
16 | + "category": "warning", |
17 | + "id": 1, |
18 | + "resource_uri": "/MAAS/api/2.0/notifications/1/" |
19 | + }, |
20 | + { |
21 | + "ident": null, |
22 | + "user": null, |
23 | + "users": false, |
24 | + "admins": true, |
25 | + "message": "Attention admins! Core critical! Meltdown imminent! Evacuate habitat immediately!", |
26 | + "context": { |
27 | + "name-oeIkwE": "value-cY9eNX" |
28 | + }, |
29 | + "category": "error", |
30 | + "id": 2, |
31 | + "resource_uri": "/MAAS/api/2.0/notifications/2/" |
32 | + }, |
33 | + { |
34 | + "ident": null, |
35 | + "user": { |
36 | + "is_superuser": true, |
37 | + "username": "admin", |
38 | + "email": "NN7ER2rH6x@example.com", |
39 | + "is_local": true, |
40 | + "resource_uri": "/MAAS/api/2.0/users/admin/" |
41 | + }, |
42 | + "users": false, |
43 | + "admins": false, |
44 | + "message": "Greetings, {name}! Get away from the habitat for the weekend and visit the Mare Nubium with MAAS Tours. Use the code METAL to claim a special gift!", |
45 | + "context": { |
46 | + "name": "Admin" |
47 | + }, |
48 | + "category": "info", |
49 | + "id": 5, |
50 | + "resource_uri": "/MAAS/api/2.0/notifications/5/" |
51 | + }, |
52 | + { |
53 | + "ident": "maas-import-pxe-files script", |
54 | + "user": null, |
55 | + "users": false, |
56 | + "admins": true, |
57 | + "message": "Boot image import process not started. Machines will not be able to\nprovision without boot images. Visit the\n<a href=\"http://localhost:5240/MAAS/#/images\">boot images</a> page to start the import.\n", |
58 | + "context": {}, |
59 | + "category": "error", |
60 | + "id": 1409, |
61 | + "resource_uri": "/MAAS/api/2.0/notifications/1409/" |
62 | + }, |
63 | + { |
64 | + "ident": "clusters", |
65 | + "user": null, |
66 | + "users": false, |
67 | + "admins": true, |
68 | + "message": "One rack controller is not yet connected to the region. Visit the <a href=\"/MAAS/#/controllers\">rack controllers page</a> for more information.", |
69 | + "context": {}, |
70 | + "category": "error", |
71 | + "id": 1410, |
72 | + "resource_uri": "/MAAS/api/2.0/notifications/1410/" |
73 | + }, |
74 | + { |
75 | + "ident": "controller_out_of_date_mfhrw8", |
76 | + "user": null, |
77 | + "users": false, |
78 | + "admins": true, |
79 | + "message": "Controller <a href='#/node/controller/{system_id}'>{hostname}</a> is running an older version of MAAS (less than 2.3.0).", |
80 | + "context": { |
81 | + "hostname": "happy-region", |
82 | + "latest_version": "", |
83 | + "system_id": "mfhrw8" |
84 | + }, |
85 | + "category": "warning", |
86 | + "id": 1411, |
87 | + "resource_uri": "/MAAS/api/2.0/notifications/1411/" |
88 | + }, |
89 | + { |
90 | + "ident": "controller_out_of_date_ekf6cn", |
91 | + "user": null, |
92 | + "users": false, |
93 | + "admins": true, |
94 | + "message": "Controller <a href='#/node/controller/{system_id}'>{hostname}</a> is running an older version of MAAS (less than 2.3.0).", |
95 | + "context": { |
96 | + "hostname": "happy-rack", |
97 | + "latest_version": "", |
98 | + "system_id": "ekf6cn" |
99 | + }, |
100 | + "category": "warning", |
101 | + "id": 1412, |
102 | + "resource_uri": "/MAAS/api/2.0/notifications/1412/" |
103 | + } |
104 | + ], |
105 | + "notifications-create": { |
106 | + "ident": null, |
107 | + "user": null, |
108 | + "users": true, |
109 | + "admins": false, |
110 | + "message": "This is a test message.", |
111 | + "context": {}, |
112 | + "category": "info", |
113 | + "id": 1413, |
114 | + "resource_uri": "/MAAS/api/2.0/notifications/1413/" |
115 | + }, |
116 | + "notifications-read-by-id": { |
117 | + "ident": null, |
118 | + "user": null, |
119 | + "users": true, |
120 | + "admins": false, |
121 | + "message": "This is a test message.", |
122 | + "context": {}, |
123 | + "category": "info", |
124 | + "id": 1414, |
125 | + "resource_uri": "/MAAS/api/2.0/notifications/1414/" |
126 | + }, |
127 | + "notifications-update": { |
128 | + "ident": null, |
129 | + "user": null, |
130 | + "users": true, |
131 | + "admins": false, |
132 | + "message": "This is an updated test message.", |
133 | + "context": {}, |
134 | + "category": "info", |
135 | + "id": 1414, |
136 | + "resource_uri": "/MAAS/api/2.0/notifications/1414/" |
137 | + } |
138 | +} |
139 | diff --git a/src/maasserver/api/notification.py b/src/maasserver/api/notification.py |
140 | index 6def843..edf059f 100644 |
141 | --- a/src/maasserver/api/notification.py |
142 | +++ b/src/maasserver/api/notification.py |
143 | @@ -44,36 +44,58 @@ class NotificationsHandler(OperationsHandler): |
144 | return ('notifications_handler', []) |
145 | |
146 | def read(self, request): |
147 | - """List notifications relevant to the invoking user. |
148 | + """@description-title List notifications |
149 | + @description List notifications relevant to the invoking user. |
150 | |
151 | Notifications that have been dismissed are *not* returned. |
152 | + |
153 | + @success (http-status-code) "server-success" 200 |
154 | + @success (json) "success-json" A JSON object containing a list of |
155 | + notification objects. |
156 | + @success-example "success-json" [exkey=notifications-read] placeholder |
157 | + text |
158 | """ |
159 | return Notification.objects.find_for_user(request.user).order_by('id') |
160 | |
161 | @admin_method |
162 | def create(self, request): |
163 | - """Create a notification. |
164 | + """@description-title Create a notification |
165 | + @description Create a new notification. |
166 | |
167 | This is available to admins *only*. |
168 | |
169 | - :param message: The message for this notification. May contain basic |
170 | - HTML; this will be sanitised before display. |
171 | - :param context: Optional JSON context. The root object *must* be an |
172 | - object (i.e. a mapping). The values herein can be referenced by |
173 | - `message` with Python's "format" (not %) codes. |
174 | - :param category: Optional category. Choose from: error, warning, |
175 | - success, or info. Defaults to info. |
176 | - |
177 | - :param ident: Optional unique identifier for this notification. |
178 | - :param user: Optional user ID this notification is intended for. By |
179 | - default it will not be targeted to any individual user. |
180 | - :param users: Optional boolean, true to notify all users, defaults to |
181 | - false, i.e. not targeted to all users. |
182 | - :param admins: Optional boolean, true to notify all admins, defaults to |
183 | - false, i.e. not targeted to all admins. |
184 | - |
185 | - Note: if neither user nor users nor admins is set, the notification |
186 | - will not be seen by anyone. |
187 | + Note: One of the ``user``, ``users`` or ``admins`` parameters must be |
188 | + set to True for the notification to be visible to anyone. |
189 | + |
190 | + @param (string) "message" [required=true] The message for this |
191 | + notification. May contain basic HTML, such as formatting. This string |
192 | + will be sanitised before display so that it doesn't break MAAS HTML. |
193 | + |
194 | + @param (string) "context" [required=false] Optional JSON context. The |
195 | + root object *must* be an object (i.e. a mapping). The values herein can |
196 | + be referenced by ``message`` with Python's "format" (not %) codes. |
197 | + |
198 | + @param (string) "category" [required=false] Choose from: ``error``, |
199 | + ``warning``, ``success``, or ``info``. Defaults to ``info``. |
200 | + |
201 | + @param (string) "ident" [required=false] Unique identifier for this |
202 | + notification. |
203 | + |
204 | + @param (string) "user" [required=false] User ID this notification is |
205 | + intended for. By default it will not be targeted to any individual |
206 | + user. |
207 | + |
208 | + @param (boolean) "users" [required=false] True to notify all users, |
209 | + defaults to false, i.e. not targeted to all users. |
210 | + |
211 | + @param (boolean) "admins" [required=false] True to notify all admins, |
212 | + defaults to false, i.e. not targeted to all admins. |
213 | + |
214 | + @success (http-status-code) "server-success" 200 |
215 | + @success (json) "success-json" A JSON object containing a new |
216 | + notification object. |
217 | + @success-example "success-json" [exkey=notifications-create] |
218 | + placeholder text |
219 | """ |
220 | form = NotificationForm(data=request.data) |
221 | if form.is_valid(): |
222 | @@ -92,7 +114,22 @@ class NotificationHandler(OperationsHandler): |
223 | fields = DISPLAYED_NOTIFICATION_FIELDS |
224 | |
225 | def read(self, request, id): |
226 | - """Read a specific notification.""" |
227 | + """@description-title Read a notification |
228 | + @description Read a notification with the given id. |
229 | + |
230 | + @param (int) "{id}" [required=true] The notification id. |
231 | + |
232 | + @success (http-status-code) "server-success" 200 |
233 | + @success (json) "success-json" A JSON object containing the requested |
234 | + notification object. |
235 | + @success-example "success-json" [exkey=notifications-read-by-id] |
236 | + placeholder text |
237 | + |
238 | + @error (http-status-code) "404" 404 |
239 | + @error (content) "not-found" The requested notification is not found. |
240 | + @error-example "not-found" |
241 | + Not Found |
242 | + """ |
243 | notification = get_object_or_404(Notification, id=id) |
244 | if notification.is_relevant_to(request.user): |
245 | return notification |
246 | @@ -103,9 +140,50 @@ class NotificationHandler(OperationsHandler): |
247 | |
248 | @admin_method |
249 | def update(self, request, id): |
250 | - """Update a specific notification. |
251 | + """@description-title Update a notification |
252 | + @description Update a notification with a given id. |
253 | + |
254 | + This is available to admins *only*. |
255 | + |
256 | + Note: One of the ``user``, ``users`` or ``admins`` parameters must be |
257 | + set to True for the notification to be visible to anyone. |
258 | + |
259 | + @param (int) "{id}" [required=true] The notification id. |
260 | |
261 | - See `NotificationsHandler.create` for field information. |
262 | + @param (string) "message" [required=true] The message for this |
263 | + notification. May contain basic HTML, such as formatting. This string |
264 | + will be sanitised before display so that it doesn't break MAAS HTML. |
265 | + |
266 | + @param (string) "context" [required=false] Optional JSON context. The |
267 | + root object *must* be an object (i.e. a mapping). The values herein can |
268 | + be referenced by ``message`` with Python's "format" (not %) codes. |
269 | + |
270 | + @param (string) "category" [required=false] Choose from: ``error``, |
271 | + ``warning``, ``success``, or ``info``. Defaults to ``info``. |
272 | + |
273 | + @param (string) "ident" [required=false] Unique identifier for this |
274 | + notification. |
275 | + |
276 | + @param (string) "user" [required=false] User ID this notification is |
277 | + intended for. By default it will not be targeted to any individual |
278 | + user. |
279 | + |
280 | + @param (boolean) "users" [required=false] True to notify all users, |
281 | + defaults to false, i.e. not targeted to all users. |
282 | + |
283 | + @param (boolean) "admins" [required=false] True to notify all admins, |
284 | + defaults to false, i.e. not targeted to all admins. |
285 | + |
286 | + @success (http-status-code) "server-success" 200 |
287 | + @success (json) "success-json" A JSON object containing the updated |
288 | + notification object. |
289 | + @success-example "success-json" [exkey=notifications-update] |
290 | + placeholder text |
291 | + |
292 | + @error (http-status-code) "404" 404 |
293 | + @error (content) "not-found" The requested notification is not found. |
294 | + @error-example "not-found" |
295 | + Not Found |
296 | """ |
297 | notification = get_object_or_404(Notification, id=id) |
298 | form = NotificationForm( |
299 | @@ -117,19 +195,41 @@ class NotificationHandler(OperationsHandler): |
300 | |
301 | @admin_method |
302 | def delete(self, request, id): |
303 | - """Delete a specific notification.""" |
304 | + """@description-title Delete a notification |
305 | + @description Delete a notification with a given id. |
306 | + |
307 | + @param (int) "{id}" [required=true] The notification id. |
308 | + |
309 | + @success (http-status-code) "server-success" 204 |
310 | + |
311 | + @error (http-status-code) "404" 404 |
312 | + @error (content) "not-found" The requested notification is not found. |
313 | + @error-example "not-found" |
314 | + Not Found |
315 | + """ |
316 | notification = get_object_or_404(Notification, id=id) |
317 | notification.delete() |
318 | return rc.DELETED |
319 | |
320 | @operation(idempotent=False) |
321 | def dismiss(self, request, id): |
322 | - """Dismiss a specific notification. |
323 | - |
324 | - Returns HTTP 403 FORBIDDEN if this notification is not relevant |
325 | - (targeted) to the invoking user. |
326 | + """@description-title Dismiss a notification |
327 | + @description Dismiss a notification with the given id. |
328 | |
329 | It is safe to call multiple times for the same notification. |
330 | + |
331 | + @param (int) "{id}" [required=true] The notification id. |
332 | + |
333 | + @success (http-status-code) "server-success" 200 |
334 | + |
335 | + @error (http-status-code) "403" 403 |
336 | + @error (content) "no-perms" The notification is not relevant to the |
337 | + invoking user. |
338 | + |
339 | + @error (http-status-code) "404" 404 |
340 | + @error (content) "not-found" The requested notification is not found. |
341 | + @error-example "not-found" |
342 | + Not Found |
343 | """ |
344 | notification = get_object_or_404(Notification, id=id) |
345 | if notification.is_relevant_to(request.user): |
I'm good with this branch. Just a few comments below; I'll leave it up to you regarding whether or not you think action is required on them.