1
0
mirror of https://github.com/etesync/server synced 2024-11-26 02:38:15 +00:00

Cleanup validation errors.

This commit is contained in:
Tom Hacohen 2020-12-28 17:39:51 +02:00
parent 10ff303b75
commit 3d438b9591
2 changed files with 51 additions and 9 deletions

View File

@ -10,7 +10,7 @@ from fastapi import APIRouter, Depends, status
from django_etebase import models from django_etebase import models
from .authentication import get_authenticated_user from .authentication import get_authenticated_user
from .exceptions import HttpError, transform_validation_error, PermissionDenied from .exceptions import HttpError, transform_validation_error, PermissionDenied, ValidationError
from .msgpack import MsgpackRoute from .msgpack import MsgpackRoute
from .stoken_handler import filter_by_stoken_and_limit, filter_by_stoken, get_stoken_obj, get_queryset_stoken from .stoken_handler import filter_by_stoken_and_limit, filter_by_stoken, get_stoken_obj, get_queryset_stoken
from .utils import ( from .utils import (
@ -151,10 +151,11 @@ class ItemDepIn(BaseModel):
item = models.CollectionItem.objects.get(uid=self.uid) item = models.CollectionItem.objects.get(uid=self.uid)
etag = self.etag etag = self.etag
if item.etag != etag: if item.etag != etag:
raise HttpError( raise ValidationError(
"wrong_etag", "wrong_etag",
"Wrong etag. Expected {} got {}".format(item.etag, etag), "Wrong etag. Expected {} got {}".format(item.etag, etag),
status_code=status.HTTP_409_CONFLICT, status_code=status.HTTP_409_CONFLICT,
field=self.uid,
) )
@ -164,8 +165,19 @@ class ItemBatchIn(BaseModel):
def validate_db(self): def validate_db(self):
if self.deps is not None: if self.deps is not None:
errors: t.List[HttpError] = []
for dep in self.deps: for dep in self.deps:
dep.validate_db() try:
dep.validate_db()
except ValidationError as e:
errors.append(e)
if len(errors) > 0:
raise ValidationError(
code="dep_failed",
detail="Dependencies failed to validate",
errors=errors,
status_code=status.HTTP_409_CONFLICT,
)
@sync_to_async @sync_to_async
@ -293,7 +305,7 @@ def _create(data: CollectionIn, user: User):
try: try:
instance.validate_unique() instance.validate_unique()
except django_exceptions.ValidationError: except django_exceptions.ValidationError:
raise HttpError( raise ValidationError(
"unique_uid", "Collection with this uid already exists", status_code=status.HTTP_409_CONFLICT "unique_uid", "Collection with this uid already exists", status_code=status.HTTP_409_CONFLICT
) )
instance.save() instance.save()
@ -353,10 +365,11 @@ def item_create(item_model: CollectionItemIn, collection: models.Collection, val
return instance return instance
if validate_etag and cur_etag != etag: if validate_etag and cur_etag != etag:
raise HttpError( raise ValidationError(
"wrong_etag", "wrong_etag",
"Wrong etag. Expected {} got {}".format(cur_etag, etag), "Wrong etag. Expected {} got {}".format(cur_etag, etag),
status_code=status.HTTP_409_CONFLICT, status_code=status.HTTP_409_CONFLICT,
field=uid,
) )
if not created: if not created:
@ -426,12 +439,22 @@ def item_bulk_common(data: ItemBatchIn, user: User, stoken: t.Optional[str], uid
if stoken is not None and stoken != collection_object.stoken: if stoken is not None and stoken != collection_object.stoken:
raise HttpError("stale_stoken", "Stoken is too old", status_code=status.HTTP_409_CONFLICT) raise HttpError("stale_stoken", "Stoken is too old", status_code=status.HTTP_409_CONFLICT)
# XXX-TOM: make sure we return compatible errors
data.validate_db() data.validate_db()
for item in data.items:
item_create(item, collection_object, validate_etag)
return None errors: t.List[HttpError] = []
for item in data.items:
try:
item_create(item, collection_object, validate_etag)
except ValidationError as e:
errors.append(e)
if len(errors) > 0:
raise ValidationError(
code="item_failed",
detail="Items failed to validate",
errors=errors,
status_code=status.HTTP_409_CONFLICT,
)
@item_router.get( @item_router.get(

View File

@ -9,12 +9,18 @@ class HttpErrorField(BaseModel):
code: str code: str
detail: str detail: str
class Config:
orm_mode = True
class HttpErrorOut(BaseModel): class HttpErrorOut(BaseModel):
code: str code: str
detail: str detail: str
errors: t.Optional[t.List[HttpErrorField]] errors: t.Optional[t.List[HttpErrorField]]
class Config:
orm_mode = True
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):
@ -73,6 +79,19 @@ class HttpError(CustomHttpException):
return HttpErrorOut(code=self.code, errors=self.errors, detail=self.detail).dict() return HttpErrorOut(code=self.code, errors=self.errors, detail=self.detail).dict()
class ValidationError(HttpError):
def __init__(
self,
code: str,
detail: str,
status_code: int = status.HTTP_400_BAD_REQUEST,
errors: t.Optional[t.List["HttpError"]] = None,
field: t.Optional[str] = None,
):
self.field = field
super().__init__(code=code, detail=detail, errors=errors, status_code=status_code)
def flatten_errors(field_name, errors) -> t.List[HttpError]: def flatten_errors(field_name, errors) -> t.List[HttpError]:
ret = [] ret = []
if isinstance(errors, dict): if isinstance(errors, dict):