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:
parent
a84d744794
commit
9546c10250
@ -94,6 +94,9 @@ class AuthorsController(RestController):
|
|||||||
if id == 997:
|
if id == 997:
|
||||||
raise NonHttpException(id)
|
raise NonHttpException(id)
|
||||||
|
|
||||||
|
if id == 996:
|
||||||
|
raise wsme.exc.ClientSideError('Disabled ID', status_code=403)
|
||||||
|
|
||||||
if id == 911:
|
if id == 911:
|
||||||
return wsme.api.Response(Author(),
|
return wsme.api.Response(Author(),
|
||||||
status_code=401)
|
status_code=401)
|
||||||
|
@ -5,7 +5,7 @@ import pecan
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
|
|
||||||
used_status_codes = [400, 401, 404, 500]
|
used_status_codes = [400, 401, 403, 404, 500]
|
||||||
http_response_messages = {}
|
http_response_messages = {}
|
||||||
for code in used_status_codes:
|
for code in used_status_codes:
|
||||||
http_response_messages[code] = '%s %s' % (code, http_client.responses[code])
|
http_response_messages[code] = '%s %s' % (code, http_client.responses[code])
|
||||||
@ -96,14 +96,14 @@ class TestWS(FunctionalTest):
|
|||||||
)
|
)
|
||||||
self.assertEqual(res.status, expected_status)
|
self.assertEqual(res.status, expected_status)
|
||||||
a = json.loads(res.body.decode('utf-8'))
|
a = json.loads(res.body.decode('utf-8'))
|
||||||
assert a['faultcode'] == 'Server'
|
assert a['faultcode'] == 'Client'
|
||||||
|
|
||||||
res = self.app.get(
|
res = self.app.get(
|
||||||
'/authors/998.xml',
|
'/authors/998.xml',
|
||||||
expect_errors=True
|
expect_errors=True
|
||||||
)
|
)
|
||||||
self.assertEqual(res.status, expected_status)
|
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):
|
def test_custom_non_http_clientside_error(self):
|
||||||
expected_status_code = 500
|
expected_status_code = 500
|
||||||
@ -123,6 +123,24 @@ class TestWS(FunctionalTest):
|
|||||||
self.assertEqual(res.status, expected_status)
|
self.assertEqual(res.status, expected_status)
|
||||||
assert '<faultcode>Server</faultcode>' in res.body.decode('utf-8')
|
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):
|
def test_non_default_response(self):
|
||||||
expected_status_code = 401
|
expected_status_code = 401
|
||||||
expected_status = http_response_messages[expected_status_code]
|
expected_status = http_response_messages[expected_status_code]
|
||||||
|
@ -181,5 +181,5 @@ class WSMECorniceTestCase(unittest.TestCase):
|
|||||||
expect_errors=True
|
expect_errors=True
|
||||||
)
|
)
|
||||||
print resp.body
|
print resp.body
|
||||||
self.assertEquals(resp.json['faultcode'], 'Server')
|
self.assertEquals(resp.json['faultcode'], 'Client')
|
||||||
self.assertEquals(resp.status_code, 401)
|
self.assertEquals(resp.status_code, 401)
|
||||||
|
@ -138,7 +138,7 @@ class FlaskrTestCase(unittest.TestCase):
|
|||||||
headers={'Accept': 'application/xml'}
|
headers={'Accept': 'application/xml'}
|
||||||
)
|
)
|
||||||
assert r.status_code == 403, r.status_code
|
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>'
|
'<faultstring>403: Forbidden</faultstring>'
|
||||||
'<debuginfo /></error>')
|
'<debuginfo /></error>')
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ class FlaskrTestCase(unittest.TestCase):
|
|||||||
headers={'Accept': 'application/xml'}
|
headers={'Accept': 'application/xml'}
|
||||||
)
|
)
|
||||||
assert r.status_code == 412, r.status_code
|
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>'
|
'<faultstring>FOO!</faultstring>'
|
||||||
'<debuginfo /></error>')
|
'<debuginfo /></error>')
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ class TestController(testutil.TGTest):
|
|||||||
assert response.status_int == 400
|
assert response.status_int == 400
|
||||||
assert simplejson.loads(response.body) == {
|
assert simplejson.loads(response.body) == {
|
||||||
"debuginfo": None,
|
"debuginfo": None,
|
||||||
"faultcode": "Server",
|
"faultcode": "Client",
|
||||||
"faultstring": "(400, 'Cannot divide by zero!')"
|
"faultstring": "(400, 'Cannot divide by zero!')"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ class TestController(testutil.TGTest):
|
|||||||
expect_errors=True
|
expect_errors=True
|
||||||
)
|
)
|
||||||
assert response.status_int == 400
|
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>(400, 'Cannot divide by zero!')"
|
||||||
"</faultstring><debuginfo /></error>")
|
"</faultstring><debuginfo /></error>")
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import logging
|
|||||||
import wsme.exc
|
import wsme.exc
|
||||||
import wsme.types
|
import wsme.types
|
||||||
|
|
||||||
|
from wsme import utils
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -200,9 +202,12 @@ class Response(object):
|
|||||||
def format_exception(excinfo, debug=False):
|
def format_exception(excinfo, debug=False):
|
||||||
"""Extract informations that can be sent to the client."""
|
"""Extract informations that can be sent to the client."""
|
||||||
error = excinfo[1]
|
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",
|
r = dict(faultcode="Client",
|
||||||
faultstring=error.faultstring)
|
faultstring=faultstring)
|
||||||
log.warning("Client-side error: %s" % r['faultstring'])
|
log.warning("Client-side error: %s" % r['faultstring'])
|
||||||
r['debuginfo'] = None
|
r['debuginfo'] = None
|
||||||
return r
|
return r
|
||||||
|
@ -4,8 +4,9 @@ from wsme.utils import _
|
|||||||
|
|
||||||
|
|
||||||
class ClientSideError(RuntimeError):
|
class ClientSideError(RuntimeError):
|
||||||
def __init__(self, msg=None):
|
def __init__(self, msg=None, status_code=400):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
self.code = status_code
|
||||||
super(ClientSideError, self).__init__(self.faultstring)
|
super(ClientSideError, self).__init__(self.faultstring)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from wsme.utils import parse_isodate, parse_isotime, parse_isodatetime
|
from wsme import utils
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(unittest.TestCase):
|
class TestUtils(unittest.TestCase):
|
||||||
@ -18,9 +18,9 @@ class TestUtils(unittest.TestCase):
|
|||||||
'2012-02-30',
|
'2012-02-30',
|
||||||
]
|
]
|
||||||
for s, d in good_dates:
|
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:
|
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):
|
def test_parse_isotime(self):
|
||||||
good_times = [
|
good_times = [
|
||||||
@ -35,9 +35,9 @@ class TestUtils(unittest.TestCase):
|
|||||||
'00:54:60',
|
'00:54:60',
|
||||||
]
|
]
|
||||||
for s, t in good_times:
|
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:
|
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):
|
def test_parse_isodatetime(self):
|
||||||
good_datetimes = [
|
good_datetimes = [
|
||||||
@ -54,6 +54,27 @@ class TestUtils(unittest.TestCase):
|
|||||||
'2012-13-12T00:54:60',
|
'2012-13-12T00:54:60',
|
||||||
]
|
]
|
||||||
for s, t in good_datetimes:
|
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:
|
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"
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import decimal
|
import decimal
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
from six.moves import builtins
|
from six.moves import builtins, http_client
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
@ -84,6 +84,18 @@ def parse_isodatetime(value):
|
|||||||
raise ValueError("'%s' is a out-of-range datetime" % (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:
|
try:
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -9,7 +9,7 @@ import wsme.api
|
|||||||
import wsme.rest.json
|
import wsme.rest.json
|
||||||
import wsme.rest.xml
|
import wsme.rest.xml
|
||||||
import wsme.rest.args
|
import wsme.rest.args
|
||||||
from wsmeext.utils import is_valid_code
|
from wsme.utils import is_valid_code
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import wsme.rest.xml
|
|||||||
|
|
||||||
import pecan
|
import pecan
|
||||||
|
|
||||||
from wsmeext.utils import is_valid_code
|
from wsme.utils import is_valid_code
|
||||||
|
|
||||||
|
|
||||||
class JSonRenderer(object):
|
class JSonRenderer(object):
|
||||||
@ -94,9 +94,7 @@ def wsexpose(*args, **kwargs):
|
|||||||
finally:
|
finally:
|
||||||
del exception_info
|
del exception_info
|
||||||
|
|
||||||
if data['faultcode'] == 'Client':
|
if orig_code and is_valid_code(orig_code):
|
||||||
pecan.response.status = 400
|
|
||||||
elif orig_code and is_valid_code(orig_code):
|
|
||||||
pecan.response.status = orig_code
|
pecan.response.status = orig_code
|
||||||
else:
|
else:
|
||||||
pecan.response.status = 500
|
pecan.response.status = 500
|
||||||
|
@ -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"
|
|
||||||
)
|
|
@ -14,7 +14,7 @@ from wsme.rest import validate as wsvalidate
|
|||||||
import wsme.api
|
import wsme.api
|
||||||
import wsme.rest.args
|
import wsme.rest.args
|
||||||
import wsme.rest.json
|
import wsme.rest.json
|
||||||
from wsmeext.utils import is_valid_code
|
from wsme.utils import is_valid_code
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
@ -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
|
|
Loading…
Reference in New Issue
Block a user