validation errors

master
Tal Leibman 4 years ago committed by Tom Hacohen
parent c90e92b0f0
commit 72d4a725f5

@ -18,15 +18,16 @@ from fastapi import APIRouter, Depends, status, Request, Response
from fastapi.security import APIKeyHeader from fastapi.security import APIKeyHeader
from pydantic import BaseModel from pydantic import BaseModel
from django_etebase import app_settings from django_etebase import app_settings, models
from django_etebase.exceptions import EtebaseValidationError from django_etebase.exceptions import EtebaseValidationError
from django_etebase.models import UserInfo from django_etebase.models import UserInfo
from django_etebase.serializers import UserSerializer from django_etebase.serializers import UserSerializer
from django_etebase.signals import user_signed_up
from django_etebase.token_auth.models import AuthToken from django_etebase.token_auth.models import AuthToken
from django_etebase.token_auth.models import get_default_expiry from django_etebase.token_auth.models import get_default_expiry
from django_etebase.utils import create_user from django_etebase.utils import create_user
from django_etebase.views import msgpack_encode, msgpack_decode from django_etebase.views import msgpack_encode, msgpack_decode
from .execptions import AuthenticationFailed from .execptions import AuthenticationFailed, transform_validation_error, ValidationError
from .msgpack import MsgpackResponse, MsgpackRoute from .msgpack import MsgpackResponse, MsgpackRoute
User = get_user_model() User = get_user_model()
@ -272,7 +273,7 @@ async def change_password(data: ChangePassword, request: Request, user: User = D
@sync_to_async @sync_to_async
def signup_save(data: SignupIn): def signup_save(data: SignupIn) -> User:
user_data = data.user user_data = data.user
with transaction.atomic(): with transaction.atomic():
try: try:
@ -290,17 +291,26 @@ def signup_save(data: SignupIn):
except EtebaseValidationError as e: except EtebaseValidationError as e:
raise e raise e
except django_exceptions.ValidationError as e: except django_exceptions.ValidationError as e:
self.transform_validation_error("user", e) transform_validation_error("user", e)
except Exception as e: except Exception as e:
raise EtebaseValidationError("generic", str(e)) raise EtebaseValidationError("generic", str(e))
if hasattr(instance, "userinfo"): if hasattr(instance, "userinfo"):
raise EtebaseValidationError("user_exists", "User already exists", status_code=status.HTTP_409_CONFLICT) raise ValidationError("user_exists", "User already exists", status_code=status.HTTP_409_CONFLICT)
models.UserInfo.objects.create(**validated_data, owner=instance) models.UserInfo.objects.create(**data.dict(exclude={"user"}), owner=instance)
return instance return instance
@sync_to_async
def send_user_signed_up_async(user: User, request):
user_signed_up.send(sender=user.__class__, request=request, user=user)
@authentication_router.post("/signup/") @authentication_router.post("/signup/")
async def signup(data: SignupIn): async def signup(data: SignupIn):
pass user = await signup_save(data)
# XXX-TOM
data = await login_response_data(user)
await send_user_signed_up_async(user, None)
return MsgpackResponse(content=data, status_code=status.HTTP_201_CREATED)

@ -1,8 +1,23 @@
from fastapi import status from fastapi import status
import typing as t
from pydantic import BaseModel
from django_etebase.exceptions import EtebaseValidationError from django_etebase.exceptions import EtebaseValidationError
class ValidationErrorField(BaseModel):
field: str
code: str
detail: str
class ValidationErrorOut(BaseModel):
code: str
detail: str
errors: t.Optional[t.List[ValidationErrorField]]
class CustomHttpException(Exception): class CustomHttpException(Exception):
def __init__(self, code: str, detail: str, status_code: int = status.HTTP_400_BAD_REQUEST): def __init__(self, code: str, detail: str, status_code: int = status.HTTP_400_BAD_REQUEST):
self.status_code = status_code self.status_code = status_code
@ -44,12 +59,27 @@ class PermissionDenied(CustomHttpException):
super().__init__(code=code, detail=detail, status_code=status_code) super().__init__(code=code, detail=detail, status_code=status_code)
from django_etebase.exceptions import EtebaseValidationError
class ValidationError(CustomHttpException): class ValidationError(CustomHttpException):
def __init__(self, code: str, detail: str, status_code: int = status.HTTP_400_BAD_REQUEST): def __init__(
self,
code: str,
detail: str,
status_code: int = status.HTTP_400_BAD_REQUEST,
field: t.Optional[str] = None,
errors: t.Optional[t.List["ValidationError"]] = None,
):
self.errors = errors
super().__init__(code=code, detail=detail, status_code=status_code) super().__init__(code=code, detail=detail, status_code=status_code)
@property
def as_dict(self) -> dict:
return ValidationErrorOut(code=self.code, errors=self.errors, detail=self.detail).dict()
def flatten_errors(field_name, errors): def flatten_errors(field_name, errors) -> t.List[ValidationError]:
ret = [] ret = []
if isinstance(errors, dict): if isinstance(errors, dict):
for error_key in errors: for error_key in errors:
@ -61,13 +91,7 @@ def flatten_errors(field_name, errors):
message = error.messages[0] message = error.messages[0]
else: else:
message = str(error) message = str(error)
ret.append( ret.append(dict(code=error.code, detail=message, field=field_name))
{
"field": field_name,
"code": error.code,
"detail": message,
}
)
return ret return ret
@ -78,11 +102,4 @@ def transform_validation_error(prefix, err):
errors = flatten_errors(prefix, err.error_list) errors = flatten_errors(prefix, err.error_list)
else: else:
raise EtebaseValidationError(err.code, err.message) raise EtebaseValidationError(err.code, err.message)
raise ValidationError(code="field_errors", detail="Field validations failed.") raise ValidationError(code="field_errors", detail="Field validations failed.", errors=errors)
raise serializers.ValidationError(
{
"code": "field_errors",
"detail": "Field validations failed.",
"errors": errors,
}
)

Loading…
Cancel
Save