diff --git a/tests/pecantest/test/controllers/ws.py b/tests/pecantest/test/controllers/ws.py
index 1dcb0cb..acb4402 100644
--- a/tests/pecantest/test/controllers/ws.py
+++ b/tests/pecantest/test/controllers/ws.py
@@ -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)
diff --git a/tests/pecantest/test/tests/test_ws.py b/tests/pecantest/test/tests/test_ws.py
index 4eb9607..ab274cd 100644
--- a/tests/pecantest/test/tests/test_ws.py
+++ b/tests/pecantest/test/tests/test_ws.py
@@ -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 'Server' in res.body.decode('utf-8')
+ assert 'Client' 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 'Server' 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 'Client' in res.body.decode('utf-8')
+
def test_non_default_response(self):
expected_status_code = 401
expected_status = http_response_messages[expected_status_code]
diff --git a/tests/test_cornice.py b/tests/test_cornice.py
index b950a39..c72fd90 100644
--- a/tests/test_cornice.py
+++ b/tests/test_cornice.py
@@ -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)
diff --git a/tests/test_flask.py b/tests/test_flask.py
index 33b7cca..2a34991 100644
--- a/tests/test_flask.py
+++ b/tests/test_flask.py
@@ -138,7 +138,7 @@ class FlaskrTestCase(unittest.TestCase):
headers={'Accept': 'application/xml'}
)
assert r.status_code == 403, r.status_code
- assert r.data == ('Server'
+ assert r.data == ('Client'
'403: Forbidden'
'')
@@ -155,7 +155,7 @@ class FlaskrTestCase(unittest.TestCase):
headers={'Accept': 'application/xml'}
)
assert r.status_code == 412, r.status_code
- assert r.data == ('Server'
+ assert r.data == ('Client'
'FOO!'
'')
diff --git a/tests/test_tg15.py b/tests/test_tg15.py
index 36c6860..b07db05 100644
--- a/tests/test_tg15.py
+++ b/tests/test_tg15.py
@@ -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 == ("Server"
+ assert response.body == ("Client"
"(400, 'Cannot divide by zero!')"
"")
diff --git a/wsme/api.py b/wsme/api.py
index b21eff1..c0ec72e 100644
--- a/wsme/api.py
+++ b/wsme/api.py
@@ -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
diff --git a/wsme/exc.py b/wsme/exc.py
index 4b62476..0dfc575 100644
--- a/wsme/exc.py
+++ b/wsme/exc.py
@@ -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
diff --git a/wsme/tests/test_utils.py b/wsme/tests/test_utils.py
index a9c5030..a1da20a 100644
--- a/wsme/tests/test_utils.py
+++ b/wsme/tests/test_utils.py
@@ -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"
+ )
diff --git a/wsme/utils.py b/wsme/utils.py
index 3d7837a..d7f3486 100644
--- a/wsme/utils.py
+++ b/wsme/utils.py
@@ -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:
diff --git a/wsmeext/flask.py b/wsmeext/flask.py
index 4abd3dd..b497698 100644
--- a/wsmeext/flask.py
+++ b/wsmeext/flask.py
@@ -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
diff --git a/wsmeext/pecan.py b/wsmeext/pecan.py
index e116bd7..66b9e7a 100644
--- a/wsmeext/pecan.py
+++ b/wsmeext/pecan.py
@@ -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
diff --git a/wsmeext/tests/test_utils.py b/wsmeext/tests/test_utils.py
deleted file mode 100644
index 0ed798e..0000000
--- a/wsmeext/tests/test_utils.py
+++ /dev/null
@@ -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"
- )
diff --git a/wsmeext/tg1.py b/wsmeext/tg1.py
index 16a7bdf..87399f9 100644
--- a/wsmeext/tg1.py
+++ b/wsmeext/tg1.py
@@ -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
diff --git a/wsmeext/utils.py b/wsmeext/utils.py
deleted file mode 100644
index 58b4013..0000000
--- a/wsmeext/utils.py
+++ /dev/null
@@ -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