REST API exception cleanup
diff --git a/planetstack/xosapibase.py b/planetstack/xosapibase.py
index ea84d9a..c523943 100644
--- a/planetstack/xosapibase.py
+++ b/planetstack/xosapibase.py
@@ -2,9 +2,36 @@
from rest_framework import serializers
from rest_framework import generics
from rest_framework import status
+from rest_framework.exceptions import APIException
from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
+class XOSProgrammingError(APIException):
+ status_code=400
+ def __init__(self, why="programming error", fields={}):
+ APIException.__init__(self, {"error": "XOSProgrammingError",
+ "specific_error": why,
+ "fields": fields})
+
+class XOSPermissionDenied(RestFrameworkPermissionDenied):
+ def __init__(self, why="permission error", fields={}):
+ APIException.__init__(self, {"error": "XOSPermissionDenied",
+ "specific_error": why,
+ "fields": fields})
+
+class XOSNotAuthenticated(RestFrameworkPermissionDenied):
+ def __init__(self, why="you must be authenticated to use this api", fields={}):
+ APIException.__init__(self, {"error": "XOSNotAuthenticated",
+ "specific_error": why,
+ "fields": fields})
+
+class XOSValidationError(APIException):
+ status_code=403
+ def __init__(self, why="validation error", fields={}):
+ APIException.__init__(self, {"error": "XOSValidationError",
+ "specific_error": why,
+ "fields": fields})
+
class XOSRetrieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
# To handle fine-grained field permissions, we have to check can_update
@@ -15,7 +42,7 @@
self.object = self.get_object_or_none()
if self.object is None:
- raise Exception("Use the List API for creating objects")
+ raise XOSProgrammingError("Use the List API for creating objects")
serializer = self.get_serializer(self.object, data=request.DATA,
files=request.FILES, partial=partial)
@@ -25,27 +52,21 @@
serializer.object.caller = request.user
if not serializer.is_valid():
- response = {"error": "validation",
- "specific_error": "not serializer.is_valid()",
- "reasons": serializer.errors}
- return Response(response, status=status.HTTP_400_BAD_REQUEST)
-
- try:
- self.pre_save(serializer.object)
- except ValidationError as err:
- # full_clean on model instance may be called in pre_save,
- # so we have to handle eventual errors.
- response = {"error": "validation",
- "specific_error": "ValidationError in pre_save",
- "reasons": err.message_dict}
- return Response(response, status=status.HTTP_400_BAD_REQUEST)
+ raise XOSValidationError(fields=serializer._errors)
if not serializer.object.can_update(request.user):
- return Response(status=status.HTTP_400_BAD_REQUEST)
+ raise XOSPermissionDenied()
- self.object = serializer.save(force_update=True)
- self.post_save(self.object, created=False)
- return Response(serializer.data, status=status.HTTP_200_OK)
+ if (hasattr(self, "pre_save")):
+ # rest_framework 2.x
+ self.pre_save(serializer.object)
+ self.object = serializer.save(force_update=True)
+ self.post_save(self.object, created=False)
+ else:
+ # rest_framework 3.x
+ self.perform_update(serializer)
+
+ return Response(serializer.data, status=status.HTTP_200_OK)
def destroy(self, request, *args, **kwargs):
obj = self.get_object()
@@ -64,7 +85,7 @@
# REST API drops the string attached to Django's PermissionDenied
# exception, and replaces it with a generic "Permission Denied"
if isinstance(exc, DjangoPermissionDenied):
- response=Response({'detail': str(exc)}, status=status.HTTP_403_FORBIDDEN)
+ response=Response({'detail': {"error": "PermissionDenied", "specific_error": str(exc), "fields": {}}}, status=status.HTTP_403_FORBIDDEN)
response.exception=True
return response
else:
@@ -73,21 +94,18 @@
class XOSListCreateAPIView(generics.ListCreateAPIView):
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
- if not (serializer.is_valid()):
- response = {"error": "validation",
- "specific_error": "not serializer.is_valid()",
- "reasons": serializer.errors}
- return Response(response, status=status.HTTP_400_BAD_REQUEST)
+
+ # In rest_framework 3.x: we can pass raise_exception=True instead of
+ # raising the exception ourselves
+ if not serializer.is_valid():
+ raise XOSValidationError(fields=serializer._errors)
# now do XOS can_update permission checking
obj = serializer.object
obj.caller = request.user
if not obj.can_update(request.user):
- response = {"error": "validation",
- "specific_error": "failed can_update",
- "reasons": []}
- return Response(response, status=status.HTTP_400_BAD_REQUEST)
+ raise XOSPermissionDenied()
# stuff below is from generics.ListCreateAPIView
@@ -108,7 +126,7 @@
# REST API drops the string attached to Django's PermissionDenied
# exception, and replaces it with a generic "Permission Denied"
if isinstance(exc, DjangoPermissionDenied):
- response=Response({'detail': str(exc)}, status=status.HTTP_403_FORBIDDEN)
+ response=Response({'detail': {"error": "PermissionDenied", "specific_error": str(exc), "fields": {}}}, status=status.HTTP_403_FORBIDDEN)
response.exception=True
return response
else: