Merge lp:~gmb/maas/fix-bug-1394746 into lp:~maas-committers/maas/trunk
- fix-bug-1394746
- Merge into trunk
Proposed by
Graham Binns
Status: | Merged |
---|---|
Merged at revision: | 3392 |
Proposed branch: | lp:~gmb/maas/fix-bug-1394746 |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
713 lines (+138/-61) 16 files modified
src/maasserver/api/boot_resources.py (+2/-2) src/maasserver/api/boot_source_selections.py (+3/-3) src/maasserver/api/boot_sources.py (+3/-3) src/maasserver/api/license_keys.py (+3/-3) src/maasserver/api/maas.py (+2/-2) src/maasserver/api/networks.py (+6/-6) src/maasserver/api/node_group_interfaces.py (+3/-3) src/maasserver/api/node_groups.py (+7/-7) src/maasserver/api/nodes.py (+11/-13) src/maasserver/api/ssh_keys.py (+2/-2) src/maasserver/api/ssl_keys.py (+2/-2) src/maasserver/api/tags.py (+5/-7) src/maasserver/api/utils.py (+7/-5) src/maasserver/api/zones.py (+3/-3) src/maasserver/exceptions.py (+22/-0) src/maasserver/tests/test_exceptions.py (+57/-0) |
To merge this branch: | bzr merge lp:~gmb/maas/fix-bug-1394746 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+243016@code.launchpad.net |
Commit message
Add a MAASAPIValidati
All the callsites that used ValidationError in maasserver.api.* have been updated to use the new exception class.
Description of the change
To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote : | # |
On 27 November 2014 at 11:17, Gavin Panella <email address hidden>
wrote:
> Looks good, but make_http_
>
Aw, darn, really? (Yes)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/maasserver/api/boot_resources.py' |
2 | --- src/maasserver/api/boot_resources.py 2014-09-04 20:01:15 +0000 |
3 | +++ src/maasserver/api/boot_resources.py 2014-11-27 12:10:49 +0000 |
4 | @@ -21,7 +21,6 @@ |
5 | import httplib |
6 | import os |
7 | |
8 | -from django.core.exceptions import ValidationError |
9 | from django.core.files.uploadedfile import SimpleUploadedFile |
10 | from django.core.urlresolvers import reverse |
11 | from django.http import HttpResponse |
12 | @@ -40,6 +39,7 @@ |
13 | from maasserver.exceptions import ( |
14 | MAASAPIBadRequest, |
15 | MAASAPIForbidden, |
16 | + MAASAPIValidationError, |
17 | ) |
18 | from maasserver.forms import ( |
19 | BootResourceForm, |
20 | @@ -205,7 +205,7 @@ |
21 | else: |
22 | form = BootResourceNoContentForm(data=data) |
23 | if not form.is_valid(): |
24 | - raise ValidationError(form.errors) |
25 | + raise MAASAPIValidationError(form.errors) |
26 | resource = form.save() |
27 | |
28 | # If an upload contained the full file, then we can have the clusters |
29 | |
30 | === modified file 'src/maasserver/api/boot_source_selections.py' |
31 | --- src/maasserver/api/boot_source_selections.py 2014-10-23 15:26:37 +0000 |
32 | +++ src/maasserver/api/boot_source_selections.py 2014-11-27 12:10:49 +0000 |
33 | @@ -18,9 +18,9 @@ |
34 | ] |
35 | |
36 | |
37 | -from django.core.exceptions import ValidationError |
38 | from django.shortcuts import get_object_or_404 |
39 | from maasserver.api.support import OperationsHandler |
40 | +from maasserver.exceptions import MAASAPIValidationError |
41 | from maasserver.forms import BootSourceSelectionForm |
42 | from maasserver.models import ( |
43 | BootSource, |
44 | @@ -72,7 +72,7 @@ |
45 | if form.is_valid(): |
46 | return form.save() |
47 | else: |
48 | - raise ValidationError(form.errors) |
49 | + raise MAASAPIValidationError(form.errors) |
50 | |
51 | def delete(self, request, boot_source_id, id): |
52 | """Delete a specific boot source.""" |
53 | @@ -166,7 +166,7 @@ |
54 | if form.is_valid(): |
55 | return form.save() |
56 | else: |
57 | - raise ValidationError(form.errors) |
58 | + raise MAASAPIValidationError(form.errors) |
59 | |
60 | |
61 | class BootSourceSelectionsBackwardHandler(BootSourceSelectionsHandler): |
62 | |
63 | === modified file 'src/maasserver/api/boot_sources.py' |
64 | --- src/maasserver/api/boot_sources.py 2014-09-04 14:51:02 +0000 |
65 | +++ src/maasserver/api/boot_sources.py 2014-11-27 12:10:49 +0000 |
66 | @@ -20,11 +20,11 @@ |
67 | from base64 import b64encode |
68 | import httplib |
69 | |
70 | -from django.core.exceptions import ValidationError |
71 | from django.core.urlresolvers import reverse |
72 | from django.http import HttpResponse |
73 | from django.shortcuts import get_object_or_404 |
74 | from maasserver.api.support import OperationsHandler |
75 | +from maasserver.exceptions import MAASAPIValidationError |
76 | from maasserver.forms import BootSourceForm |
77 | from maasserver.models import BootSource |
78 | from piston.emitters import JSONEmitter |
79 | @@ -94,7 +94,7 @@ |
80 | if form.is_valid(): |
81 | return form.save() |
82 | else: |
83 | - raise ValidationError(form.errors) |
84 | + raise MAASAPIValidationError(form.errors) |
85 | |
86 | def delete(self, request, id): |
87 | """Delete a specific boot source.""" |
88 | @@ -176,7 +176,7 @@ |
89 | stream, mimetype='application/json; charset=utf-8', |
90 | status=httplib.CREATED) |
91 | else: |
92 | - raise ValidationError(form.errors) |
93 | + raise MAASAPIValidationError(form.errors) |
94 | |
95 | |
96 | class BootSourcesBackwardHandler(BootSourcesHandler): |
97 | |
98 | === modified file 'src/maasserver/api/license_keys.py' |
99 | --- src/maasserver/api/license_keys.py 2014-08-16 16:47:28 +0000 |
100 | +++ src/maasserver/api/license_keys.py 2014-11-27 12:10:49 +0000 |
101 | @@ -18,9 +18,9 @@ |
102 | ] |
103 | |
104 | |
105 | -from django.core.exceptions import ValidationError |
106 | from django.shortcuts import get_object_or_404 |
107 | from maasserver.api.support import OperationsHandler |
108 | +from maasserver.exceptions import MAASAPIValidationError |
109 | from maasserver.forms import LicenseKeyForm |
110 | from maasserver.models import LicenseKey |
111 | from maasserver.utils.orm import get_one |
112 | @@ -52,7 +52,7 @@ |
113 | data = {} |
114 | form = LicenseKeyForm(data=data) |
115 | if not form.is_valid(): |
116 | - raise ValidationError(form.errors) |
117 | + raise MAASAPIValidationError(form.errors) |
118 | return form.save() |
119 | |
120 | @classmethod |
121 | @@ -89,7 +89,7 @@ |
122 | data = {} |
123 | form = LicenseKeyForm(instance=license_key, data=data) |
124 | if not form.is_valid(): |
125 | - raise ValidationError(form.errors) |
126 | + raise MAASAPIValidationError(form.errors) |
127 | return form.save() |
128 | |
129 | def delete(self, request, osystem, distro_series): |
130 | |
131 | === modified file 'src/maasserver/api/maas.py' |
132 | --- src/maasserver/api/maas.py 2014-08-18 12:39:48 +0000 |
133 | +++ src/maasserver/api/maas.py 2014-11-27 12:10:49 +0000 |
134 | @@ -17,7 +17,6 @@ |
135 | ] |
136 | |
137 | |
138 | -from django.core.exceptions import ValidationError |
139 | from django.http import HttpResponse |
140 | from formencode import validators |
141 | from maasserver.api.support import ( |
142 | @@ -25,6 +24,7 @@ |
143 | OperationsHandler, |
144 | ) |
145 | from maasserver.api.utils import get_mandatory_param |
146 | +from maasserver.exceptions import MAASAPIValidationError |
147 | from maasserver.forms_settings import ( |
148 | get_config_doc, |
149 | get_config_form, |
150 | @@ -56,7 +56,7 @@ |
151 | value = get_mandatory_param(request.data, 'value') |
152 | form = get_config_form(name, {name: value}) |
153 | if not form.is_valid(): |
154 | - raise ValidationError(form.errors) |
155 | + raise MAASAPIValidationError(form.errors) |
156 | form.save() |
157 | return rc.ALL_OK |
158 | |
159 | |
160 | === modified file 'src/maasserver/api/networks.py' |
161 | --- src/maasserver/api/networks.py 2014-09-17 03:14:08 +0000 |
162 | +++ src/maasserver/api/networks.py 2014-11-27 12:10:49 +0000 |
163 | @@ -18,7 +18,6 @@ |
164 | ] |
165 | |
166 | |
167 | -from django.core.exceptions import ValidationError |
168 | from django.shortcuts import get_object_or_404 |
169 | from maasserver.api.support import ( |
170 | admin_method, |
171 | @@ -26,6 +25,7 @@ |
172 | OperationsHandler, |
173 | ) |
174 | from maasserver.enum import NODE_PERMISSION |
175 | +from maasserver.exceptions import MAASAPIValidationError |
176 | from maasserver.forms import ( |
177 | NetworkConnectMACsForm, |
178 | NetworkDisconnectMACsForm, |
179 | @@ -77,7 +77,7 @@ |
180 | instance=network, data=request.data, |
181 | delete_macs_if_not_present=False) |
182 | if not form.is_valid(): |
183 | - raise ValidationError(form.errors) |
184 | + raise MAASAPIValidationError(form.errors) |
185 | return form.save() |
186 | |
187 | @admin_method |
188 | @@ -107,7 +107,7 @@ |
189 | network = get_object_or_404(Network, name=name) |
190 | form = NetworkConnectMACsForm(network=network, data=request.data) |
191 | if not form.is_valid(): |
192 | - raise ValidationError(form.errors) |
193 | + raise MAASAPIValidationError(form.errors) |
194 | form.save() |
195 | |
196 | @admin_method |
197 | @@ -123,7 +123,7 @@ |
198 | network = get_object_or_404(Network, name=name) |
199 | form = NetworkDisconnectMACsForm(network=network, data=request.data) |
200 | if not form.is_valid(): |
201 | - raise ValidationError(form.errors) |
202 | + raise MAASAPIValidationError(form.errors) |
203 | form.save() |
204 | |
205 | @operation(idempotent=True) |
206 | @@ -165,7 +165,7 @@ |
207 | """ |
208 | form = NetworksListingForm(data=request.GET) |
209 | if not form.is_valid(): |
210 | - raise ValidationError(form.errors) |
211 | + raise MAASAPIValidationError(form.errors) |
212 | return form.filter_networks(Network.objects.all()) |
213 | |
214 | @admin_method |
215 | @@ -186,7 +186,7 @@ |
216 | """ |
217 | form = NetworkForm(request.data) |
218 | if not form.is_valid(): |
219 | - raise ValidationError(form.errors) |
220 | + raise MAASAPIValidationError(form.errors) |
221 | return form.save() |
222 | |
223 | @classmethod |
224 | |
225 | === modified file 'src/maasserver/api/node_group_interfaces.py' |
226 | --- src/maasserver/api/node_group_interfaces.py 2014-11-24 18:23:26 +0000 |
227 | +++ src/maasserver/api/node_group_interfaces.py 2014-11-27 12:10:49 +0000 |
228 | @@ -18,13 +18,13 @@ |
229 | ] |
230 | |
231 | |
232 | -from django.core.exceptions import ValidationError |
233 | from django.shortcuts import get_object_or_404 |
234 | from maasserver.api.node_groups import check_nodegroup_access |
235 | from maasserver.api.support import ( |
236 | operation, |
237 | OperationsHandler, |
238 | ) |
239 | +from maasserver.exceptions import MAASAPIValidationError |
240 | from maasserver.forms import NodeGroupInterfaceForm |
241 | from maasserver.models import ( |
242 | NodeGroup, |
243 | @@ -107,7 +107,7 @@ |
244 | if form.is_valid(): |
245 | return form.save() |
246 | else: |
247 | - raise ValidationError(form.errors) |
248 | + raise MAASAPIValidationError(form.errors) |
249 | |
250 | @classmethod |
251 | def resource_uri(cls, nodegroup=None): |
252 | @@ -189,7 +189,7 @@ |
253 | if form.is_valid(): |
254 | return form.save() |
255 | else: |
256 | - raise ValidationError(form.errors) |
257 | + raise MAASAPIValidationError(form.errors) |
258 | |
259 | def delete(self, request, uuid, name): |
260 | """Delete a specific NodeGroupInterface. |
261 | |
262 | === modified file 'src/maasserver/api/node_groups.py' |
263 | --- src/maasserver/api/node_groups.py 2014-11-24 17:55:55 +0000 |
264 | +++ src/maasserver/api/node_groups.py 2014-11-27 12:10:49 +0000 |
265 | @@ -21,10 +21,7 @@ |
266 | import httplib |
267 | |
268 | import bson |
269 | -from django.core.exceptions import ( |
270 | - PermissionDenied, |
271 | - ValidationError, |
272 | - ) |
273 | +from django.core.exceptions import PermissionDenied |
274 | from django.http import HttpResponse |
275 | from django.shortcuts import get_object_or_404 |
276 | from formencode import validators |
277 | @@ -43,7 +40,10 @@ |
278 | from maasserver.clusterrpc.power_parameters import ( |
279 | get_all_power_types_from_clusters, |
280 | ) |
281 | -from maasserver.exceptions import Unauthorized |
282 | +from maasserver.exceptions import ( |
283 | + MAASAPIValidationError, |
284 | + Unauthorized, |
285 | + ) |
286 | from maasserver.forms import ( |
287 | DownloadProgressForm, |
288 | NodeGroupEdit, |
289 | @@ -212,7 +212,7 @@ |
290 | if form.is_valid(): |
291 | return form.save() |
292 | else: |
293 | - raise ValidationError(form.errors) |
294 | + raise MAASAPIValidationError(form.errors) |
295 | |
296 | @admin_method |
297 | @operation(idempotent=False) |
298 | @@ -356,7 +356,7 @@ |
299 | |
300 | form = DownloadProgressForm(data=request.data, instance=download) |
301 | if not form.is_valid(): |
302 | - raise ValidationError(form.errors) |
303 | + raise MAASAPIValidationError(form.errors) |
304 | form.save() |
305 | |
306 | return HttpResponse(status=httplib.OK) |
307 | |
308 | === modified file 'src/maasserver/api/nodes.py' |
309 | --- src/maasserver/api/nodes.py 2014-11-24 18:23:26 +0000 |
310 | +++ src/maasserver/api/nodes.py 2014-11-27 12:10:49 +0000 |
311 | @@ -22,10 +22,7 @@ |
312 | import bson |
313 | import crochet |
314 | from django.conf import settings |
315 | -from django.core.exceptions import ( |
316 | - PermissionDenied, |
317 | - ValidationError, |
318 | - ) |
319 | +from django.core.exceptions import PermissionDenied |
320 | from django.http import HttpResponse |
321 | from django.shortcuts import get_object_or_404 |
322 | from maasserver import locks |
323 | @@ -50,6 +47,7 @@ |
324 | ) |
325 | from maasserver.exceptions import ( |
326 | MAASAPIBadRequest, |
327 | + MAASAPIValidationError, |
328 | NodesNotAvailable, |
329 | NodeStateViolation, |
330 | PowerProblem, |
331 | @@ -246,7 +244,7 @@ |
332 | if form.is_valid(): |
333 | return form.save() |
334 | else: |
335 | - raise ValidationError(form.errors) |
336 | + raise MAASAPIValidationError(form.errors) |
337 | |
338 | def delete(self, request, system_id): |
339 | """Delete a specific Node. |
340 | @@ -341,7 +339,7 @@ |
341 | if form.is_valid(): |
342 | form.save() |
343 | else: |
344 | - raise ValidationError(form.errors) |
345 | + raise MAASAPIValidationError(form.errors) |
346 | |
347 | try: |
348 | node.start(request.user, user_data=user_data) |
349 | @@ -396,7 +394,7 @@ |
350 | node = form.save(allow_redirect=False) |
351 | return node |
352 | else: |
353 | - raise ValidationError(form.errors) |
354 | + raise MAASAPIValidationError(form.errors) |
355 | |
356 | @operation(idempotent=True) |
357 | def details(self, request, system_id): |
358 | @@ -622,7 +620,7 @@ |
359 | if given_arch and '/' in given_arch: |
360 | if given_subarch: |
361 | # Architecture with a '/' and a subarchitecture: error. |
362 | - raise ValidationError('Subarchitecture cannot be specified twice.') |
363 | + raise MAASAPIValidationError('Subarchitecture cannot be specified twice.') |
364 | # Architecture with a '/' in it: use normally. |
365 | elif given_arch: |
366 | if given_subarch: |
367 | @@ -644,7 +642,7 @@ |
368 | # We insist on this to protect command-line API users who |
369 | # are manually enlisting nodes. You can't use the origin's |
370 | # IP address to indicate in which nodegroup the new node belongs. |
371 | - raise ValidationError( |
372 | + raise MAASAPIValidationError( |
373 | "'autodetect_nodegroup' must be specified if 'nodegroup' " |
374 | "parameter missing") |
375 | nodegroup = find_nodegroup(request) |
376 | @@ -660,7 +658,7 @@ |
377 | maaslog.info("%s: Enlisted new node", node.hostname) |
378 | return node |
379 | else: |
380 | - raise ValidationError(form.errors) |
381 | + raise MAASAPIValidationError(form.errors) |
382 | |
383 | |
384 | class AnonNodesHandler(AnonymousOperationsHandler): |
385 | @@ -940,7 +938,7 @@ |
386 | invalid_macs = [ |
387 | mac for mac in match_macs if MAC_RE.match(mac) is None] |
388 | if len(invalid_macs) != 0: |
389 | - raise ValidationError( |
390 | + raise MAASAPIValidationError( |
391 | "Invalid MAC address(es): %s" % ", ".join(invalid_macs)) |
392 | |
393 | # Fetch nodes and apply filters. |
394 | @@ -1040,7 +1038,7 @@ |
395 | request.user.username, request.data) |
396 | |
397 | if not form.is_valid(): |
398 | - raise ValidationError(form.errors) |
399 | + raise MAASAPIValidationError(form.errors) |
400 | |
401 | # This lock prevents a node we've picked as available from |
402 | # becoming unavailable before our transaction commits. |
403 | @@ -1085,7 +1083,7 @@ |
404 | } |
405 | form = BulkNodeActionForm(request.user, data=data) |
406 | if not form.is_valid(): |
407 | - raise ValidationError(form.errors) |
408 | + raise MAASAPIValidationError(form.errors) |
409 | form.save() |
410 | |
411 | @admin_method |
412 | |
413 | === modified file 'src/maasserver/api/ssh_keys.py' |
414 | --- src/maasserver/api/ssh_keys.py 2014-11-20 23:47:49 +0000 |
415 | +++ src/maasserver/api/ssh_keys.py 2014-11-27 12:10:49 +0000 |
416 | @@ -19,13 +19,13 @@ |
417 | |
418 | import httplib |
419 | |
420 | -from django.core.exceptions import ValidationError |
421 | from django.http import HttpResponse |
422 | from django.shortcuts import get_object_or_404 |
423 | from maasserver.api.support import ( |
424 | operation, |
425 | OperationsHandler, |
426 | ) |
427 | +from maasserver.exceptions import MAASAPIValidationError |
428 | from maasserver.forms import SSHKeyForm |
429 | from maasserver.models import SSHKey |
430 | from piston.emitters import JSONEmitter |
431 | @@ -64,7 +64,7 @@ |
432 | stream, mimetype='application/json; charset=utf-8', |
433 | status=httplib.CREATED) |
434 | else: |
435 | - raise ValidationError(form.errors) |
436 | + raise MAASAPIValidationError(form.errors) |
437 | |
438 | @classmethod |
439 | def resource_uri(cls, *args, **kwargs): |
440 | |
441 | === modified file 'src/maasserver/api/ssl_keys.py' |
442 | --- src/maasserver/api/ssl_keys.py 2014-11-21 21:23:39 +0000 |
443 | +++ src/maasserver/api/ssl_keys.py 2014-11-27 12:10:49 +0000 |
444 | @@ -19,13 +19,13 @@ |
445 | |
446 | import httplib |
447 | |
448 | -from django.core.exceptions import ValidationError |
449 | from django.http import HttpResponse |
450 | from django.shortcuts import get_object_or_404 |
451 | from maasserver.api.support import ( |
452 | operation, |
453 | OperationsHandler, |
454 | ) |
455 | +from maasserver.exceptions import MAASAPIValidationError |
456 | from maasserver.forms import SSLKeyForm |
457 | from maasserver.models import SSLKey |
458 | from piston.emitters import JSONEmitter |
459 | @@ -64,7 +64,7 @@ |
460 | stream, mimetype='application/json; charset=utf-8', |
461 | status=httplib.CREATED) |
462 | else: |
463 | - raise ValidationError(form.errors) |
464 | + raise MAASAPIValidationError(form.errors) |
465 | |
466 | @classmethod |
467 | def resource_uri(cls, *args, **kwargs): |
468 | |
469 | === modified file 'src/maasserver/api/tags.py' |
470 | --- src/maasserver/api/tags.py 2014-11-21 21:23:39 +0000 |
471 | +++ src/maasserver/api/tags.py 2014-11-27 12:10:49 +0000 |
472 | @@ -20,10 +20,7 @@ |
473 | |
474 | import httplib |
475 | |
476 | -from django.core.exceptions import ( |
477 | - PermissionDenied, |
478 | - ValidationError, |
479 | - ) |
480 | +from django.core.exceptions import PermissionDenied |
481 | from django.db.utils import DatabaseError |
482 | from django.http import HttpResponse |
483 | from maasserver.api.node_groups import check_nodegroup_access |
484 | @@ -33,6 +30,7 @@ |
485 | ) |
486 | from maasserver.api.utils import get_list_from_dict_or_multidict |
487 | from maasserver.enum import NODE_PERMISSION |
488 | +from maasserver.exceptions import MAASAPIValidationError |
489 | from maasserver.forms import TagForm |
490 | from maasserver.models import ( |
491 | Node, |
492 | @@ -89,10 +87,10 @@ |
493 | new_tag.save() |
494 | form.save_m2m() |
495 | except DatabaseError as e: |
496 | - raise ValidationError(e) |
497 | + raise MAASAPIValidationError(e) |
498 | return new_tag |
499 | else: |
500 | - raise ValidationError(form.errors) |
501 | + raise MAASAPIValidationError(form.errors) |
502 | |
503 | def delete(self, request, name): |
504 | """Delete a specific Tag. |
505 | @@ -223,7 +221,7 @@ |
506 | if form.is_valid(): |
507 | return form.save() |
508 | else: |
509 | - raise ValidationError(form.errors) |
510 | + raise MAASAPIValidationError(form.errors) |
511 | |
512 | @operation(idempotent=True) |
513 | def list(self, request): |
514 | |
515 | === modified file 'src/maasserver/api/utils.py' |
516 | --- src/maasserver/api/utils.py 2014-08-16 05:43:33 +0000 |
517 | +++ src/maasserver/api/utils.py 2014-11-27 12:10:49 +0000 |
518 | @@ -23,10 +23,12 @@ |
519 | 'get_overridden_query_dict', |
520 | ] |
521 | |
522 | -from django.core.exceptions import ValidationError |
523 | from django.http import QueryDict |
524 | from formencode.validators import Invalid |
525 | -from maasserver.exceptions import Unauthorized |
526 | +from maasserver.exceptions import ( |
527 | + MAASAPIValidationError, |
528 | + Unauthorized, |
529 | + ) |
530 | from piston.models import Token |
531 | |
532 | |
533 | @@ -69,12 +71,12 @@ |
534 | """ |
535 | value = data.get(key, None) |
536 | if value is None: |
537 | - raise ValidationError("No provided %s!" % key) |
538 | + raise MAASAPIValidationError("No provided %s!" % key) |
539 | if validator is not None: |
540 | try: |
541 | return validator.to_python(value) |
542 | except Invalid as e: |
543 | - raise ValidationError("Invalid %s: %s" % (key, e.msg)) |
544 | + raise MAASAPIValidationError("Invalid %s: %s" % (key, e.msg)) |
545 | else: |
546 | return value |
547 | |
548 | @@ -103,7 +105,7 @@ |
549 | try: |
550 | return validator.to_python(value) |
551 | except Invalid as e: |
552 | - raise ValidationError("Invalid %s: %s" % (key, e.msg)) |
553 | + raise MAASAPIValidationError("Invalid %s: %s" % (key, e.msg)) |
554 | else: |
555 | return value |
556 | |
557 | |
558 | === modified file 'src/maasserver/api/zones.py' |
559 | --- src/maasserver/api/zones.py 2014-11-24 17:55:55 +0000 |
560 | +++ src/maasserver/api/zones.py 2014-11-27 12:10:49 +0000 |
561 | @@ -17,12 +17,12 @@ |
562 | 'ZonesHandler', |
563 | ] |
564 | |
565 | -from django.core.exceptions import ValidationError |
566 | from django.shortcuts import get_object_or_404 |
567 | from maasserver.api.support import ( |
568 | admin_method, |
569 | OperationsHandler, |
570 | ) |
571 | +from maasserver.exceptions import MAASAPIValidationError |
572 | from maasserver.forms import ZoneForm |
573 | from maasserver.models import Zone |
574 | from maasserver.utils.orm import get_one |
575 | @@ -63,7 +63,7 @@ |
576 | zone = get_object_or_404(Zone, name=name) |
577 | form = ZoneForm(instance=zone, data=request.data) |
578 | if not form.is_valid(): |
579 | - raise ValidationError(form.errors) |
580 | + raise MAASAPIValidationError(form.errors) |
581 | return form.save() |
582 | |
583 | @admin_method |
584 | @@ -110,7 +110,7 @@ |
585 | if form.is_valid(): |
586 | return form.save() |
587 | else: |
588 | - raise ValidationError(form.errors) |
589 | + raise MAASAPIValidationError(form.errors) |
590 | |
591 | def read(self, request): |
592 | """List zones. |
593 | |
594 | === modified file 'src/maasserver/exceptions.py' |
595 | --- src/maasserver/exceptions.py 2014-11-25 10:18:03 +0000 |
596 | +++ src/maasserver/exceptions.py 2014-11-27 12:10:49 +0000 |
597 | @@ -31,10 +31,12 @@ |
598 | |
599 | import httplib |
600 | |
601 | +from django.core.exceptions import ValidationError |
602 | from django.http import ( |
603 | HttpResponse, |
604 | HttpResponseRedirect, |
605 | ) |
606 | +import simplejson as json |
607 | |
608 | |
609 | class MAASException(Exception): |
610 | @@ -78,6 +80,26 @@ |
611 | api_error = httplib.FORBIDDEN |
612 | |
613 | |
614 | +class MAASAPIValidationError(MAASAPIBadRequest, ValidationError): |
615 | + """A validation error raised during a MAAS API request.""" |
616 | + |
617 | + def make_http_response(self): |
618 | + """Create an :class:`HttpResponse` representing this exception.""" |
619 | + mimetype = b"application/json" |
620 | + if hasattr(self, 'error_dict'): |
621 | + messages = json.dumps(self.message_dict) |
622 | + elif len(self.messages) == 1: |
623 | + messages = self.messages[0] |
624 | + mimetype = b"text/plain" |
625 | + else: |
626 | + messages = json.dumps(self.messages) |
627 | + |
628 | + encoding = b'utf-8' |
629 | + return HttpResponse( |
630 | + status=self.api_error, content=messages, |
631 | + mimetype=b"%s; charset=%s" % (mimetype, encoding)) |
632 | + |
633 | + |
634 | class Unauthorized(MAASAPIException): |
635 | """HTTP error 401: Unauthorized. Login required.""" |
636 | api_error = httplib.UNAUTHORIZED |
637 | |
638 | === modified file 'src/maasserver/tests/test_exceptions.py' |
639 | --- src/maasserver/tests/test_exceptions.py 2014-07-18 17:05:57 +0000 |
640 | +++ src/maasserver/tests/test_exceptions.py 2014-11-27 12:10:49 +0000 |
641 | @@ -18,11 +18,14 @@ |
642 | |
643 | from maasserver.exceptions import ( |
644 | MAASAPIBadRequest, |
645 | + MAASAPIValidationError, |
646 | Redirect, |
647 | ) |
648 | from maasserver.testing import extract_redirect |
649 | from maastesting.factory import factory |
650 | from maastesting.testcase import MAASTestCase |
651 | +import simplejson as json |
652 | +from testtools.matchers import Equals |
653 | |
654 | |
655 | class TestExceptions(MAASTestCase): |
656 | @@ -40,3 +43,57 @@ |
657 | exception = Redirect(target) |
658 | response = exception.make_http_response() |
659 | self.assertEqual(target, extract_redirect(response)) |
660 | + |
661 | + |
662 | +class TestMAASAPIValidationError(MAASTestCase): |
663 | + """Tests for the `MAASAPIValidationError` exception class.""" |
664 | + |
665 | + def test_returns_http_response(self): |
666 | + error = factory.make_string() |
667 | + exception = MAASAPIValidationError(error) |
668 | + response = exception.make_http_response() |
669 | + self.assertEqual( |
670 | + (httplib.BAD_REQUEST, error), |
671 | + (response.status_code, response.content)) |
672 | + |
673 | + def test_returns_textual_response_if_message_is_a_string(self): |
674 | + error = factory.make_string() |
675 | + exception = MAASAPIValidationError(error) |
676 | + response = exception.make_http_response() |
677 | + self.assertEqual( |
678 | + "text/plain; charset=utf-8", response.get("Content-Type")) |
679 | + |
680 | + def test_returns_json_response_if_message_is_a_list(self): |
681 | + errors = [ |
682 | + factory.make_string(), |
683 | + factory.make_string(), |
684 | + ] |
685 | + exception = MAASAPIValidationError(errors) |
686 | + response = exception.make_http_response() |
687 | + self.expectThat( |
688 | + response.get("Content-Type"), |
689 | + Equals("application/json; charset=utf-8")) |
690 | + self.expectThat(response.content, Equals(json.dumps(errors))) |
691 | + |
692 | + def test_if_message_is_single_item_list_returns_only_first_message(self): |
693 | + errors = [ |
694 | + factory.make_string(), |
695 | + ] |
696 | + exception = MAASAPIValidationError(errors) |
697 | + response = exception.make_http_response() |
698 | + self.expectThat( |
699 | + response.get("Content-Type"), |
700 | + Equals("text/plain; charset=utf-8")) |
701 | + self.expectThat(response.content, Equals(errors[0])) |
702 | + |
703 | + def test_returns_json_response_if_message_is_a_dict(self): |
704 | + errors = { |
705 | + 'error_1': [factory.make_string()], |
706 | + 'error_2': [factory.make_string()], |
707 | + } |
708 | + exception = MAASAPIValidationError(errors) |
709 | + response = exception.make_http_response() |
710 | + self.expectThat( |
711 | + response.get("Content-Type"), |
712 | + Equals("application/json; charset=utf-8")) |
713 | + self.expectThat(response.content, Equals(json.dumps(errors))) |
Looks good, but make_http_ response( ) needs a test or two.