Exceptions: have correct code/status_code for every error.

This commit is contained in:
Tom Hacohen 2020-07-30 10:13:24 +03:00
parent 04231ebfe5
commit c0575cb64c
3 changed files with 33 additions and 14 deletions

View File

@ -0,0 +1,10 @@
from rest_framework import serializers, status
class EtebaseValidationError(serializers.ValidationError):
def __init__(self, code, detail, status_code=status.HTTP_400_BAD_REQUEST):
super().__init__({
'code': code,
'detail': detail,
})
self.status_code = status_code

View File

@ -18,10 +18,12 @@ from django.core.files.base import ContentFile
from django.core import exceptions as django_exceptions from django.core import exceptions as django_exceptions
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import transaction from django.db import transaction
from rest_framework import serializers from rest_framework import serializers, status
from . import models from . import models
from .utils import get_user_queryset, create_user from .utils import get_user_queryset, create_user
from .exceptions import EtebaseValidationError
User = get_user_model() User = get_user_model()
@ -40,7 +42,7 @@ def process_revisions_for_item(item, revision_data):
chunk_obj.save() chunk_obj.save()
else: else:
if chunk_obj is None: if chunk_obj is None:
raise serializers.ValidationError('Tried to create a new chunk without content') raise EtebaseValidationError('chunk_no_content', 'Tried to create a new chunk without content')
chunks_objs.append(chunk_obj) chunks_objs.append(chunk_obj)
@ -115,7 +117,7 @@ class ChunksField(serializers.RelatedField):
def to_internal_value(self, data): def to_internal_value(self, data):
if data[0] is None or data[1] is None: if data[0] is None or data[1] is None:
raise serializers.ValidationError('null is not allowed') raise EtebaseValidationError('null is not allowed')
return (data[0], b64decode_or_bytes(data[1])) return (data[0], b64decode_or_bytes(data[1]))
@ -161,7 +163,7 @@ class CollectionItemSerializer(serializers.ModelSerializer):
cur_etag = instance.etag if not created else None cur_etag = instance.etag if not created else None
if validate_etag and cur_etag != etag: if validate_etag and cur_etag != etag:
raise serializers.ValidationError('Wrong etag. Expected {} got {}'.format(cur_etag, etag)) raise EtebaseValidationError('wrong_etag', 'Wrong etag. Expected {} got {}'.format(cur_etag, etag), status_code=status.HTTP_409_CONFLICT)
if not created: if not created:
# We don't have to use select_for_update here because the unique constraint on current guards against # We don't have to use select_for_update here because the unique constraint on current guards against
@ -190,7 +192,7 @@ class CollectionItemDepSerializer(serializers.ModelSerializer):
item = self.__class__.Meta.model.objects.get(uid=data['uid']) item = self.__class__.Meta.model.objects.get(uid=data['uid'])
etag = data['etag'] etag = data['etag']
if item.etag != etag: if item.etag != etag:
raise serializers.ValidationError('Wrong etag. Expected {} got {}'.format(item.etag, etag)) raise EtebaseValidationError('wrong_etag', 'Wrong etag. Expected {} got {}'.format(item.etag, etag), status_code=status.HTTP_409_CONFLICT)
return data return data
@ -232,7 +234,7 @@ class CollectionSerializer(serializers.ModelSerializer):
with transaction.atomic(): with transaction.atomic():
if etag is not None: if etag is not None:
raise serializers.ValidationError('etag is not None') raise EtebaseValidationError('bad_etag', 'etag is not null')
instance.save() instance.save()
main_item = models.CollectionItem.objects.create(**main_item_data, collection=instance) main_item = models.CollectionItem.objects.create(**main_item_data, collection=instance)
@ -297,7 +299,7 @@ class CollectionInvitationSerializer(serializers.ModelSerializer):
request = self.context['request'] request = self.context['request']
if request.user.username == value.lower(): if request.user.username == value.lower():
raise serializers.ValidationError('Inviting yourself is not allowed') raise EtebaseValidationError('no_self_invite', 'Inviting yourself is not allowed')
return value return value
def create(self, validated_data): def create(self, validated_data):
@ -393,15 +395,15 @@ class AuthenticationSignupSerializer(serializers.Serializer):
try: try:
instance = create_user(**user_data, password=None, first_name=user_data['username'], view=view) instance = create_user(**user_data, password=None, first_name=user_data['username'], view=view)
except Exception as e: except Exception as e:
raise serializers.ValidationError(e) raise EtebaseValidationError('generic', str(e))
if hasattr(instance, 'userinfo'): if hasattr(instance, 'userinfo'):
raise serializers.ValidationError('User already exists') raise EtebaseValidationError('user_exists', 'User already exists', status_code=status.HTTP_409_CONFLICT)
try: try:
instance.clean_fields() instance.clean_fields()
except django_exceptions.ValidationError as e: except django_exceptions.ValidationError as e:
raise serializers.ValidationError(e) raise EtebaseValidationError('generic', str(e))
# FIXME: send email verification # FIXME: send email verification
models.UserInfo.objects.create(**validated_data, owner=instance) models.UserInfo.objects.create(**validated_data, owner=instance)

View File

@ -72,7 +72,7 @@ from .serializers import (
UserSerializer, UserSerializer,
) )
from .utils import get_user_queryset from .utils import get_user_queryset
from .exceptions import EtebaseValidationError
User = get_user_model() User = get_user_model()
@ -111,7 +111,14 @@ class BaseViewSet(viewsets.ModelViewSet):
stoken = self.get_stoken_obj_id(request) stoken = self.get_stoken_obj_id(request)
if stoken is not None: if stoken is not None:
return get_object_or_404(Stoken.objects.all(), uid=stoken) try:
return Stoken.objects.get(uid=stoken)
except Stoken.DoesNotExist:
raise EtebaseValidationError({
'code': 'bad_stoken',
'detail': 'Invalid stoken.',
},
status_code=status.HTTP_400_BAD_REQUEST)
return None return None
@ -363,7 +370,7 @@ class CollectionItemViewSet(BaseViewSet):
if stoken is not None and stoken != collection_object.stoken: if stoken is not None and stoken != collection_object.stoken:
content = {'code': 'stale_stoken', 'detail': 'Stoken is too old'} content = {'code': 'stale_stoken', 'detail': 'Stoken is too old'}
return Response(content, status=status.HTTP_400_BAD_REQUEST) return Response(content, status=status.HTTP_409_CONFLICT)
items = request.data.get('items') items = request.data.get('items')
deps = request.data.get('deps', None) deps = request.data.get('deps', None)
@ -387,7 +394,7 @@ class CollectionItemViewSet(BaseViewSet):
"items": serializer.errors, "items": serializer.errors,
"deps": deps_serializer.errors if deps is not None else [], "deps": deps_serializer.errors if deps is not None else [],
}, },
status=status.HTTP_400_BAD_REQUEST) status=status.HTTP_409_CONFLICT)
class CollectionItemChunkViewSet(viewsets.ViewSet): class CollectionItemChunkViewSet(viewsets.ViewSet):