[CORD-967] Returning errors in JSON
Change-Id: I35f48f6a8ec1931b1242a78f6777fffc184f54df
diff --git a/xos/coreapi/apihelper.py b/xos/coreapi/apihelper.py
index a2a2a95..f79701f 100644
--- a/xos/coreapi/apihelper.py
+++ b/xos/coreapi/apihelper.py
@@ -26,7 +26,7 @@
context = kwargs["context"]
else:
context = args[2]
- context.set_details(str(e))
+ context.set_details(e.json_detail)
if (type(e) == XOSPermissionDenied):
context.set_code(grpc.StatusCode.PERMISSION_DENIED)
elif (type(e) == XOSValidationError):
diff --git a/xos/xos/exceptions.py b/xos/xos/exceptions.py
index b3631e4..805944b 100644
--- a/xos/xos/exceptions.py
+++ b/xos/xos/exceptions.py
@@ -1,71 +1,138 @@
+import json
from rest_framework.exceptions import APIException
from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
+def _get_json_error_details(data):
+ """
+ Convert error details to JSON
+ """
+ if isinstance(data, dict):
+ ret = {
+ key: value for key, value in data.items()
+ }
+ elif isinstance(data, list):
+ ret = [
+ item for item in data
+ ]
+
+ return json.dumps(ret)
+
+
class XOSProgrammingError(APIException):
status_code=400
def __init__(self, why="programming error", fields={}):
- APIException.__init__(self, {"error": "XOSProgrammingError",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSProgrammingError",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSPermissionDenied(RestFrameworkPermissionDenied):
def __init__(self, why="permission error", fields={}):
- APIException.__init__(self, {"error": "XOSPermissionDenied",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSPermissionDenied",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSNotAuthenticated(RestFrameworkPermissionDenied):
status_code=401
def __init__(self, why="you must be authenticated to use this api", fields={}):
- APIException.__init__(self, {"error": "XOSNotAuthenticated",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSNotAuthenticated",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSNotFound(RestFrameworkPermissionDenied):
status_code=404
def __init__(self, why="object not found", fields={}):
- APIException.__init__(self, {"error": "XOSNotFound",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSNotFound",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSValidationError(APIException):
status_code=403
def __init__(self, why="validation error", fields={}):
- APIException.__init__(self, {"error": "XOSValidationError",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSValidationError",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSDuplicateKey(APIException):
status_code=400
def __init__(self, why="duplicate key", fields={}):
- APIException.__init__(self, {"error": "XOSDuplicateKey",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSDuplicateKey",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSMissingField(APIException):
status_code=400
def __init__(self, why="missing field", fields={}):
- APIException.__init__(self, {"error": "XOSMissingField",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSMissingField",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSConfigurationError(APIException):
status_code=400
def __init__(self, why="configuration error", fields={}):
- APIException.__init__(self, {"error": "XOSConfigurationError",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSConfigurationError",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSConflictingField(APIException):
status_code=400
def __init__(self, why="conflicting field", fields={}):
- APIException.__init__(self, {"error": "XOSMissingField",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSMissingField",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
class XOSServiceUnavailable(APIException):
status_code=503
def __init__(self, why="Service temporarily unavailable, try again later", fields={}):
- APIException.__init__(self, {"error": "XOSServiceUnavailable",
- "specific_error": why,
- "fields": fields})
+ raw_detail = {
+ "error": "XOSServiceUnavailable",
+ "specific_error": why,
+ "fields": fields
+ }
+ APIException.__init__(self, raw_detail)
+ self.raw_detail = raw_detail
+ self.json_detail = _get_json_error_details(raw_detail)
diff --git a/xos/xos/exceptions_test.py b/xos/xos/exceptions_test.py
new file mode 100644
index 0000000..ec8e448
--- /dev/null
+++ b/xos/xos/exceptions_test.py
@@ -0,0 +1,32 @@
+import unittest
+import sys
+import os
+import inspect
+import json
+sys.path.append(os.path.abspath('..'))
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
+import xos.exceptions
+from xos.exceptions import *
+
+class TestXosExceptions(unittest.TestCase):
+ """
+ Test the conversion from excenption to json
+ """
+ def test_get_json_error_details(self):
+ res = xos.exceptions._get_json_error_details({'foo': 'bar'})
+ assert res == json.dumps({"foo":"bar"})
+
+ def test_exceptions(self):
+ """
+ This test iterate over all the classes in exceptions.py and if they start with XOS
+ validate the json_detail output
+ """
+ for name, item in inspect.getmembers(xos.exceptions):
+ if inspect.isclass(item) and name.startswith('XOS'):
+ e = item('test error', {'foo': 'bar'})
+ res = e.json_detail
+ assert res == json.dumps(
+ {"fields": {"foo": "bar"}, "specific_error": "test error", "error": name})
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file