Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Friendly JSON exceptions (bug 928061, bug 928062)
Example http://pastie.org/3338663 Change-Id: I26f53488c062ebfb6e49cfcf82e0b8179a683ea8
- Loading branch information
Showing
6 changed files
with
171 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import re | ||
|
||
|
||
class Error(StandardError): | ||
"""Base error class. | ||
Child classes should define an HTTP status code, title, and a doc string. | ||
""" | ||
code = None | ||
title = None | ||
|
||
def __init__(self, message=None, **kwargs): | ||
"""Use the doc string as the error message by default.""" | ||
message = message or self.__doc__ % kwargs | ||
super(Error, self).__init__(message) | ||
|
||
def __str__(self): | ||
"""Cleans up line breaks and indentation from doc strings.""" | ||
string = super(Error, self).__str__() | ||
string = re.sub('[ \n]+', ' ', string) | ||
string = string.strip() | ||
return string | ||
|
||
|
||
class ValidationError(Error): | ||
"""Expecting to find %(attribute)s in %(target)s. | ||
The server could not comply with the request since it is either malformed | ||
or otherwise incorrect. | ||
The client is assumed to be in error. | ||
""" | ||
code = 400 | ||
title = 'Bad Request' | ||
|
||
|
||
class Unauthorized(Error): | ||
"""The request you have made requires authentication.""" | ||
code = 401 | ||
title = 'Not Authorized' | ||
|
||
|
||
class Forbidden(Error): | ||
"""You are not authorized to perform the requested action: %(action)s""" | ||
code = 403 | ||
title = 'Not Authorized' | ||
|
||
|
||
class NotFound(Error): | ||
"""Could not find: %(target)s""" | ||
code = 404 | ||
title = 'Not Found' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
import uuid | ||
import json | ||
|
||
from keystone.common import wsgi | ||
from keystone import exception | ||
from keystone import test | ||
|
||
|
||
class ExceptionTestCase(test.TestCase): | ||
def setUp(self): | ||
pass | ||
|
||
def tearDown(self): | ||
pass | ||
|
||
def assertValidJsonRendering(self, e): | ||
resp = wsgi.render_exception(e) | ||
self.assertEqual(resp.status_int, e.code) | ||
self.assertEqual(resp.status, '%s %s' % (e.code, e.title)) | ||
|
||
j = json.loads(resp.body) | ||
self.assertIsNotNone(j.get('error')) | ||
self.assertIsNotNone(j['error'].get('code')) | ||
self.assertIsNotNone(j['error'].get('title')) | ||
self.assertIsNotNone(j['error'].get('message')) | ||
self.assertNotIn('\n', j['error']['message']) | ||
self.assertNotIn(' ', j['error']['message']) | ||
self.assertTrue(type(j['error']['code']) is int) | ||
|
||
def test_validation_error(self): | ||
target = uuid.uuid4().hex | ||
attribute = uuid.uuid4().hex | ||
e = exception.ValidationError(target=target, attribute=attribute) | ||
self.assertValidJsonRendering(e) | ||
self.assertIn(target, str(e)) | ||
self.assertIn(attribute, str(e)) | ||
|
||
def test_unauthorized(self): | ||
e = exception.Unauthorized() | ||
self.assertValidJsonRendering(e) | ||
|
||
def test_forbidden(self): | ||
action = uuid.uuid4().hex | ||
e = exception.Forbidden(action=action) | ||
self.assertValidJsonRendering(e) | ||
self.assertIn(action, str(e)) | ||
|
||
def test_not_found(self): | ||
target = uuid.uuid4().hex | ||
e = exception.NotFound(target=target) | ||
self.assertValidJsonRendering(e) | ||
self.assertIn(target, str(e)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters