Skip to content
This repository was archived by the owner on Feb 10, 2024. It is now read-only.

Commit 6d489f6

Browse files
Fix validation error and update exceptions
1 parent d4a638f commit 6d489f6

File tree

4 files changed

+91
-39
lines changed

4 files changed

+91
-39
lines changed

dsjapi/decorator.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from . import exceptions
66
from . import fields
77

8+
89
def api(field=None):
910

1011
def decorator(func):
@@ -24,7 +25,7 @@ def wrappedFunc(request):
2425
try:
2526
requestData = json.loads(request.body)
2627
except ValueError as e:
27-
raise exceptions.BadRequestException("Error while parsing JSON content", {"error": str(e)})
28+
raise exceptions.BadRequestException("Error while parsing JSON content: {}".format(str(e)))
2829

2930
if field is not None:
3031
if not isinstance(field, fields.Field):
@@ -36,7 +37,7 @@ def add(v):
3637
cleanedData = v
3738
field.cleanAndAdd(requestData is not None, requestData, add)
3839
except fields.FieldException as e:
39-
raise exceptions.BadRequestException("Field exception: {}".format(e.getMessage()))
40+
raise exceptions.BadRequestException("Field exception", e)
4041
else:
4142
cleanedData = requestData
4243

@@ -55,5 +56,3 @@ def add(v):
5556
return wrappedFunc
5657

5758
return decorator
58-
59-

dsjapi/exceptions.py

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,108 @@
11
from django.http import JsonResponse, HttpResponseNotAllowed
22

3-
class ServerException(Exception):
3+
4+
class PrintableException(Exception):
5+
6+
def toDict(self):
7+
return {"type": self.__class__.__name__}
8+
9+
def __str__(self):
10+
return self.__class__.__name__
11+
12+
def __repr__(self):
13+
return repr(self.toDict())
14+
15+
16+
class ServerException(PrintableException):
417

518
def buildResponse(self):
619
raise NotImplementedError()
720

21+
822
class JSONServerException(ServerException):
923

10-
def __init__(self, status, message=None, data=None):
24+
def __init__(self, status):
1125
self._status = status
12-
self._message = message
13-
self._data = data
14-
15-
def getData(self):
16-
return self._data
1726

1827
def getStatus(self):
1928
return self._status
2029

30+
def buildResponse(self):
31+
return JsonResponse(self.toDict(), status=self._status)
32+
33+
34+
class CommonException(PrintableException):
35+
36+
def __init__(self, message=None, cause=None):
37+
self._message = message
38+
self._cause = cause
39+
40+
def getCause(self):
41+
return self._cause
42+
2143
def getMessage(self):
2244
return self._message
2345

24-
def buildResponse(self):
25-
content = {}
46+
def toDict(self):
47+
content = super().toDict()
2648
if self._message is not None:
2749
content["message"] = self._message
28-
if self._data is not None:
29-
content["data"] = self._data
30-
return JsonResponse(content, status=self._status)
50+
if self._cause is not None:
51+
content["cause"] = self._cause.toDict()
52+
return content
53+
54+
def __str__(self):
55+
txt = self.__class__.__name__
56+
if self._message is not None:
57+
txt += ": {}".format(self._message)
58+
if self._cause is not None:
59+
txt += "\nCaused by:\n{}".format(str(self._cause))
60+
return txt
61+
62+
63+
class CommonJSONServerException(JSONServerException, CommonException):
3164

65+
def __init__(self, status, message=None, cause=None):
66+
JSONServerException.__init__(self, status)
67+
CommonException.__init__(self, message, cause)
3268

33-
class AuthenticationException(JSONServerException):
3469

35-
def __init__(self, message=None, data=None):
36-
super().__init__(401, message, data)
70+
class AuthenticationException(CommonJSONServerException):
3771

72+
def __init__(self, message=None, cause=None):
73+
super().__init__(401, message, cause)
3874

39-
class BadRequestException(JSONServerException):
4075

41-
def __init__(self, message=None, data=None):
42-
super().__init__(400, message, data)
76+
class BadRequestException(CommonJSONServerException):
4377

78+
def __init__(self, message=None, cause=None):
79+
super().__init__(400, message, cause)
4480

45-
class NotFoundException(JSONServerException):
4681

47-
def __init__(self, message=None, data=None):
48-
super().__init__(404, message, data)
82+
class NotFoundException(CommonJSONServerException):
4983

84+
def __init__(self, message=None, cause=None):
85+
super().__init__(404, message, cause)
5086

51-
class ServerErrorException(JSONServerException):
5287

53-
def __init__(self, message=None, data=None):
54-
super().__init__(500, message, data)
88+
class ServerErrorException(CommonJSONServerException):
89+
90+
def __init__(self, message=None, cause=None):
91+
super().__init__(500, message, cause)
5592

5693

5794
class MethodNotAllowedException(ServerException):
5895

5996
def __init__(self, allowedMethods):
6097
self._allowedMethods = allowedMethods
6198

99+
def toDict(self):
100+
content = super().toDict()
101+
content["allowedMethods"] = self._allowedMethods
102+
return content
103+
104+
def __str__(self):
105+
return "{}\nAllowed methods:\n{}".format(self.__class__.__name__, self._allowedMethods)
106+
62107
def buildResponse(self):
63108
return HttpResponseNotAllowed(self._allowedMethods)

dsjapi/fields.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
from enum import Enum, auto
22
import dateutil
33
import re
4+
from . import exceptions
45

5-
class FieldException(Exception):
66

7-
def __init__(self, message):
8-
self._message = message
7+
class FieldException(exceptions.CommonException):
98

10-
def getMessage(self):
11-
return self._message
9+
def __init__(self, message, cause=None):
10+
super().__init__(message, cause)
1211

1312

1413
class Field:
@@ -62,9 +61,18 @@ def __init__(self, type, missing=Field.Do.RAISE, error=Field.Do.RAISE, default=N
6261
def getType(self):
6362
return self._type
6463

64+
@staticmethod
65+
def formatTypeConstraint(typeConstraint):
66+
if isinstance(typeConstraint, type):
67+
return typeConstraint.__name__
68+
elif isinstance(typeConstraint, (tuple, list)):
69+
return " or ".join(map(lambda t: t.__name__, typeConstraint))
70+
else:
71+
return "<error type>"
72+
6573
def clean(self, value):
6674
if not isinstance(value, self._type):
67-
raise FieldException("Expected type {}".format(self._type))
75+
raise FieldException("Expected type {}".format(TypeField.formatTypeConstraint(self._type)))
6876
return value
6977

7078

@@ -163,7 +171,7 @@ def clean(self, value):
163171
if isinstance(self._fields, list):
164172
fields = self._fields
165173
elif isinstance(self._fields, Field):
166-
fields = [None] * len(value)
174+
fields = [self._fields] * len(value)
167175
else:
168176
raise TypeError("Bad type fields type")
169177
items = []
@@ -172,7 +180,7 @@ def clean(self, value):
172180
try:
173181
fields[i].cleanAndAdd(True, item, items.append)
174182
except FieldException as e:
175-
raise FieldException("Field exception on item {}: {}".format(i, e.getMessage()))
183+
raise FieldException("Field exception on item {}".format(i), e)
176184
else:
177185
items.append(item)
178186
value = items
@@ -234,7 +242,7 @@ def add(v):
234242
dictionary[key] = v
235243
self._fields.cleanAndAdd(True, item, add)
236244
except FieldException as e:
237-
raise FieldException('Field exception on item "{}": {}'.format(key, e.getMessage()))
245+
raise FieldException('Field exception on item "{}"'.format(key), e)
238246
elif isinstance(self._fields, dict):
239247
for key, item in self._fields.items():
240248
try:
@@ -243,7 +251,7 @@ def add(v):
243251
present = key in value
244252
self._fields[key].cleanAndAdd(present, value[key] if present else None, add)
245253
except FieldException as e:
246-
raise FieldException('Field exception on item "{}": {}'.format(key, e.getMessage()))
254+
raise FieldException('Field exception on item "{}"'.format(key), e)
247255
unexpected = set(value.keys()) - set(self._fields.keys())
248256
if len(unexpected) > 0:
249257
raise FieldException('Unexpected fields {}'.format(unexpected))

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
setup(
1010
name='django-simple-json-api',
1111
author='Francesco Zoccheddu',
12-
version='0.0.1',
12+
version='0.0.2',
1313
description='Create simple JSON APIs with Django',
1414
long_description=_README,
1515
long_description_content_type='text/markdown',

0 commit comments

Comments
 (0)