Add custom error code to ClientSideError

Added custom error status code for ClientSideError
exception instead of hardcoded value 400.
Fixed case when user exception with client error code
formatted as server error.
Pecan extension fixed.

Change-Id: I2663db0aa88538b722eb2783d130585b0fc2335b
This commit is contained in:
Yuriy Zveryanskyy 2013-10-09 11:26:29 +03:00
parent a84d744794
commit 9546c10250
14 changed files with 83 additions and 58 deletions

View File

@ -94,6 +94,9 @@ class AuthorsController(RestController):
if id == 997:
raise NonHttpException(id)
if id == 996:
raise wsme.exc.ClientSideError('Disabled ID', status_code=403)
if id == 911:
return wsme.api.Response(Author(),
status_code=401)

View File

@ -5,7 +5,7 @@ import pecan
import six
used_status_codes = [400, 401, 404, 500]
used_status_codes = [400, 401, 403, 404, 500]
http_response_messages = {}
for code in used_status_codes:
http_response_messages[code] = '%s %s' % (code, http_client.responses[code])
@ -96,14 +96,14 @@ class TestWS(FunctionalTest):
)
self.assertEqual(res.status, expected_status)
a = json.loads(res.body.decode('utf-8'))
assert a['faultcode'] == 'Server'
assert a['faultcode'] == 'Client'
res = self.app.get(
'/authors/998.xml',
expect_errors=True
)
self.assertEqual(res.status, expected_status)
assert '<faultcode>Server</faultcode>' in res.body.decode('utf-8')
assert '<faultcode>Client</faultcode>' in res.body.decode('utf-8')
def test_custom_non_http_clientside_error(self):
expected_status_code = 500
@ -123,6 +123,24 @@ class TestWS(FunctionalTest):
self.assertEqual(res.status, expected_status)
assert '<faultcode>Server</faultcode>' in res.body.decode('utf-8')
def test_clientsideerror_status_code(self):
expected_status_code = 403
expected_status = http_response_messages[expected_status_code]
res = self.app.get(
'/authors/996.json',
expect_errors=True
)
self.assertEqual(res.status, expected_status)
a = json.loads(res.body.decode('utf-8'))
assert a['faultcode'] == 'Client'
res = self.app.get(
'/authors/996.xml',
expect_errors=True
)
self.assertEqual(res.status, expected_status)
assert '<faultcode>Client</faultcode>' in res.body.decode('utf-8')
def test_non_default_response(self):
expected_status_code = 401
expected_status = http_response_messages[expected_status_code]

View File

@ -181,5 +181,5 @@ class WSMECorniceTestCase(unittest.TestCase):
expect_errors=True
)
print resp.body
self.assertEquals(resp.json['faultcode'], 'Server')
self.assertEquals(resp.json['faultcode'], 'Client')
self.assertEquals(resp.status_code, 401)

View File

@ -138,7 +138,7 @@ class FlaskrTestCase(unittest.TestCase):
headers={'Accept': 'application/xml'}
)
assert r.status_code == 403, r.status_code
assert r.data == ('<error><faultcode>Server</faultcode>'
assert r.data == ('<error><faultcode>Client</faultcode>'
'<faultstring>403: Forbidden</faultstring>'
'<debuginfo /></error>')
@ -155,7 +155,7 @@ class FlaskrTestCase(unittest.TestCase):
headers={'Accept': 'application/xml'}
)
assert r.status_code == 412, r.status_code
assert r.data == ('<error><faultcode>Server</faultcode>'
assert r.data == ('<error><faultcode>Client</faultcode>'
'<faultstring>FOO!</faultstring>'
'<debuginfo /></error>')

View File

@ -109,7 +109,7 @@ class TestController(testutil.TGTest):
assert response.status_int == 400
assert simplejson.loads(response.body) == {
"debuginfo": None,
"faultcode": "Server",
"faultcode": "Client",
"faultstring": "(400, 'Cannot divide by zero!')"
}
@ -120,7 +120,7 @@ class TestController(testutil.TGTest):
expect_errors=True
)
assert response.status_int == 400
assert response.body == ("<error><faultcode>Server</faultcode>"
assert response.body == ("<error><faultcode>Client</faultcode>"
"<faultstring>(400, 'Cannot divide by zero!')"
"</faultstring><debuginfo /></error>")

View File

@ -6,6 +6,8 @@ import logging
import wsme.exc
import wsme.types
from wsme import utils
log = logging.getLogger(__name__)
@ -200,9 +202,12 @@ class Response(object):
def format_exception(excinfo, debug=False):
"""Extract informations that can be sent to the client."""
error = excinfo[1]
if isinstance(error, wsme.exc.ClientSideError):
code = getattr(error, 'code', None)
if code and utils.is_valid_code(code) and utils.is_client_error(code):
faultstring = error.faultstring if hasattr(error, 'faultstring') \
else str(error)
r = dict(faultcode="Client",
faultstring=error.faultstring)
faultstring=faultstring)
log.warning("Client-side error: %s" % r['faultstring'])
r['debuginfo'] = None
return r

View File

@ -4,8 +4,9 @@ from wsme.utils import _
class ClientSideError(RuntimeError):
def __init__(self, msg=None):
def __init__(self, msg=None, status_code=400):
self.msg = msg
self.code = status_code
super(ClientSideError, self).__init__(self.faultstring)
@property

View File

@ -1,7 +1,7 @@
import datetime
import unittest
from wsme.utils import parse_isodate, parse_isotime, parse_isodatetime
from wsme import utils
class TestUtils(unittest.TestCase):
@ -18,9 +18,9 @@ class TestUtils(unittest.TestCase):
'2012-02-30',
]
for s, d in good_dates:
assert parse_isodate(s) == d
assert utils.parse_isodate(s) == d
for s in ill_formatted_dates + out_of_range_dates:
self.assertRaises(ValueError, parse_isodate, s)
self.assertRaises(ValueError, utils.parse_isodate, s)
def test_parse_isotime(self):
good_times = [
@ -35,9 +35,9 @@ class TestUtils(unittest.TestCase):
'00:54:60',
]
for s, t in good_times:
assert parse_isotime(s) == t
assert utils.parse_isotime(s) == t
for s in ill_formatted_times + out_of_range_times:
self.assertRaises(ValueError, parse_isotime, s)
self.assertRaises(ValueError, utils.parse_isotime, s)
def test_parse_isodatetime(self):
good_datetimes = [
@ -54,6 +54,27 @@ class TestUtils(unittest.TestCase):
'2012-13-12T00:54:60',
]
for s, t in good_datetimes:
assert parse_isodatetime(s) == t
assert utils.parse_isodatetime(s) == t
for s in ill_formatted_datetimes + out_of_range_datetimes:
self.assertRaises(ValueError, parse_isodatetime, s)
self.assertRaises(ValueError, utils.parse_isodatetime, s)
def test_validator_with_valid_code(self):
valid_code = 404
assert (
utils.is_valid_code(valid_code),
"Valid status code not detected"
)
def test_validator_with_invalid_int_code(self):
invalid_int_code = 648
assert (
not utils.is_valid_code(invalid_int_code),
"Invalid status code not detected"
)
def test_validator_with_invalid_str_code(self):
invalid_str_code = '404'
assert (
not utils.is_valid_code(invalid_str_code),
"Invalid status code not detected"
)

View File

@ -1,7 +1,7 @@
import decimal
import datetime
import re
from six.moves import builtins
from six.moves import builtins, http_client
try:
import dateutil.parser
@ -84,6 +84,18 @@ def parse_isodatetime(value):
raise ValueError("'%s' is a out-of-range datetime" % (value))
def is_valid_code(code_value):
"""
This function checks if incoming value in http response codes range.
"""
return code_value in http_client.responses
def is_client_error(code):
""" Checks client error code (RFC 2616)."""
return 400 <= code < 500
try:
from collections import OrderedDict
except ImportError:

View File

@ -9,7 +9,7 @@ import wsme.api
import wsme.rest.json
import wsme.rest.xml
import wsme.rest.args
from wsmeext.utils import is_valid_code
from wsme.utils import is_valid_code
import flask

View File

@ -11,7 +11,7 @@ import wsme.rest.xml
import pecan
from wsmeext.utils import is_valid_code
from wsme.utils import is_valid_code
class JSonRenderer(object):
@ -94,9 +94,7 @@ def wsexpose(*args, **kwargs):
finally:
del exception_info
if data['faultcode'] == 'Client':
pecan.response.status = 400
elif orig_code and is_valid_code(orig_code):
if orig_code and is_valid_code(orig_code):
pecan.response.status = orig_code
else:
pecan.response.status = 500

View File

@ -1,22 +0,0 @@
from wsmeext.utils import is_valid_code
class TestUtils():
def test_validator_with_valid_code(self):
valid_code = 404
assert is_valid_code(valid_code), "Valid status code not detected"
def test_validator_with_invalid_int_code(self):
invalid_int_code = 648
assert (
not is_valid_code(invalid_int_code),
"Invalid status code not detected"
)
def test_validator_with_invalid_str_code(self):
invalid_str_code = '404'
assert (
not is_valid_code(invalid_str_code),
"Invalid status code not detected"
)

View File

@ -14,7 +14,7 @@ from wsme.rest import validate as wsvalidate
import wsme.api
import wsme.rest.args
import wsme.rest.json
from wsmeext.utils import is_valid_code
from wsme.utils import is_valid_code
import inspect

View File

@ -1,11 +0,0 @@
"""
This File consists of utils functions used in wsmeext module.
"""
from six.moves import http_client
def is_valid_code(code_value):
"""
This function checks if incoming value in http response codes range.
"""
return code_value in http_client.responses