Friendly JSON exceptions (bug 928061, bug 928062)
Example http://pastie.org/3338663 Change-Id: I26f53488c062ebfb6e49cfcf82e0b8179a683ea8changes/79/3879/6
parent
524d3d1c41
commit
c64a12ffc7
@ -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'
|
@ -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))
|
Loading…
Reference in New Issue