From e694d854226a88bc1a32f34d5afb7de57100924e Mon Sep 17 00:00:00 2001 From: splimterXrodina Date: Sun, 11 Sep 2022 22:24:29 +0100 Subject: [PATCH 1/3] update the api response --- backend/utils/api.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 backend/utils/api.py diff --git a/backend/utils/api.py b/backend/utils/api.py new file mode 100644 index 0000000..2ae7941 --- /dev/null +++ b/backend/utils/api.py @@ -0,0 +1,31 @@ +from pydantic import BaseModel +from typing import Any, Dict +from builtins import Exception as PyException +from enum import Enum + + +class Status(str, Enum): + success = "success" + failed = "failed" + + +class Error(BaseModel): + type: str + code: int + message: str | list[dict] + + +class Response(BaseModel): + status: Status + error: Error | None = None + data: Dict[str, Any] | BaseModel | None = None + + +class Exception(PyException): + status_code: int + error: Error + + def __init__(self, status_code: int, error: Error): + super().__init__(status_code) + self.status_code = status_code + self.error = error From 8608ed295dc35b2ef1ab73fc7930ada10e223000 Mon Sep 17 00:00:00 2001 From: splimterXrodina Date: Sun, 11 Sep 2022 22:31:51 +0100 Subject: [PATCH 2/3] update the api response --- backend/api/schemas/data.py | 20 -------------- backend/api/schemas/errors.py | 16 ++++++----- backend/api/schemas/response.py | 48 +++++++++++++-------------------- backend/main.py | 19 +++++++------ backend/utils/api.py | 31 --------------------- 5 files changed, 37 insertions(+), 97 deletions(-) delete mode 100644 backend/api/schemas/data.py delete mode 100644 backend/utils/api.py diff --git a/backend/api/schemas/data.py b/backend/api/schemas/data.py deleted file mode 100644 index b8ca9b4..0000000 --- a/backend/api/schemas/data.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Pydantic data models to be reusable anywhere""" - -from pydantic import BaseModel -from enum import Enum - - -class Status(str, Enum): - success = "success" - failed = "failed" - - -class Error(BaseModel): - type: str - code: int - message: str | list[dict] - - -class Success(BaseModel): - code: int | str - message: str diff --git a/backend/api/schemas/errors.py b/backend/api/schemas/errors.py index c9282cb..38fd990 100644 --- a/backend/api/schemas/errors.py +++ b/backend/api/schemas/errors.py @@ -1,25 +1,27 @@ """ Errors used in the API. """ -from api.schemas.data import Error -# NOT_AUTHENTICATED = Error(type="auth", code=10, message="Not authenticated") +# NOT_AUTHENTICATED = Error(err_type="auth", code=10, message="Not authenticated") + +from backend.api.schemas.response import Error + EXPIRED_TOKEN = Error( - type="auth", + err_type="auth", code=105, message="You need to renew the Access token using the refresh token", ) -VALIDATION_ERR = Error(type="validation", code=114, message="") +VALIDATION_ERR = Error(err_type="validation", code=114, message="") -ELIGIBILITY_ERR = Error(type="eligibility", code=115, message="Not eligible") +ELIGIBILITY_ERR = Error(err_type="eligibility", code=115, message="Not eligible") INVALID_ACCESS_TOKEN = Error( - type="token", code=101, message="The Access-Token is not valid" + err_type="token", code=101, message="The Access-Token is not valid" ) INVALID_TOKEN = Error( - type="auth", + err_type="auth", code=103, message="Invalid token", ) diff --git a/backend/api/schemas/response.py b/backend/api/schemas/response.py index 16d2c18..8904d91 100644 --- a/backend/api/schemas/response.py +++ b/backend/api/schemas/response.py @@ -1,38 +1,28 @@ -from typing import Dict, Any +from enum import Enum from pydantic import BaseModel import api.schemas.errors as api_errors -from .data import Status, Error, Success +from typing import Any, Dict +from builtins import Exception as PyException -# ================================== Response Models -class ApiResponse(BaseModel): - """The base ApiResponse model""" +class Status(str, Enum): + success = "success" + failed = "failed" - status: Status = Status.success - success: Success | Dict[str, Any] | BaseModel | None = None - error: Error | None = None - data: Dict[str, Any] | BaseModel | None = None - - def dict(self, *args, **kwargs) -> dict[str, Any]: - kwargs.pop("exclude_none") - return super().dict(*args, exclude_none=True, **kwargs) - class Config: - use_enum_values = True +class Error(BaseModel): + err_type: str + code: int + message: str | list[dict] - @staticmethod - def schema_extra(schema, model) -> None: - if schema.get("properties")["status"]["default"] == "success": - schema.get("properties").pop("error") - if schema.get("properties")["status"]["default"] == "failed": - schema.get("properties").pop("data") - schema.get("properties").pop("success") +class APIResponse(BaseModel): + status: Status + error: Error | None = None + data: Dict[str, Any] | BaseModel | None = None -# ================================== ErrorResponse Models -class ApiException(Exception): - """Exception customized to acts as an ErrorResponse""" +class APIException(PyException): status_code: int error: Error @@ -42,21 +32,21 @@ def __init__(self, status_code: int, error: Error): self.error = error -class INVALID_ACCESS_TOKENResponse(ApiResponse): +class INVALID_ACCESS_TOKENResponse(APIResponse): status: Status = Status.failed error: Error = api_errors.INVALID_ACCESS_TOKEN -class ExpiredTokenResponse(ApiResponse): +class ExpiredTokenResponse(APIResponse): status: Status = Status.failed error: Error = api_errors.EXPIRED_TOKEN -class ValidationErrorResponse(ApiResponse): +class ValidationErrorResponse(APIResponse): status: Status = Status.failed error: Error = api_errors.VALIDATION_ERR -class EligibilityErrorResponse(ApiResponse): +class EligibilityErrorResponse(APIResponse): status: Status = Status.failed error: Error = api_errors.ELIGIBILITY_ERR diff --git a/backend/main.py b/backend/main.py index 15f9a49..241ffda 100644 --- a/backend/main.py +++ b/backend/main.py @@ -18,8 +18,7 @@ from hypercorn.config import Config from starlette.exceptions import HTTPException as StarletteHTTPException from api.schemas import examples as api_examples -from api.schemas.data import Error, Status -from api.schemas.response import ApiException, ApiResponse +from api.schemas.response import APIException, APIResponse, Error, Status from utils.response import OurResponse from utils.logger import logger from utils.settings import settings @@ -90,9 +89,9 @@ async def my_exception_handler(_, exception): @app.exception_handler(RequestValidationError) async def validation_exception_handler(_, exc: RequestValidationError): err = jsonable_encoder({"detail": exc.errors()})["detail"] - raise ApiException( + raise APIException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - error=Error(code=422, type="validation", message=err), + error=Error(code=422, err_type="validation", message=err), ) @@ -113,11 +112,11 @@ async def middle(request: Request, call_next): ): try: response = await call_next(request) - except ApiException as ex: + except APIException as ex: response = JSONResponse( status_code=ex.status_code, content=jsonable_encoder( - ApiResponse(status=Status.failed, error=ex.error) + APIResponse(status=Status.failed, error=ex.error) ), ) stack = [ @@ -157,10 +156,10 @@ async def middle(request: Request, call_next): response = JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, content=jsonable_encoder( - ApiResponse( + APIResponse( status=Status.failed, error=Error( - type="bad request", code=112, message="Invalid request." + err_type="bad request", code=112, message="Invalid request." ), ) ), @@ -249,10 +248,10 @@ async def myoptions(): @app.patch("/{x:path}", include_in_schema=False, dependencies=[Depends(capture_body)]) @app.delete("/{x:path}", include_in_schema=False, dependencies=[Depends(capture_body)]) async def catchall(): - raise ApiException( + raise APIException( status_code=status.HTTP_404_NOT_FOUND, error=Error( - type="catchall", code=501, message="Requested method or path is invalid" + err_type="catchall", code=501, message="Requested method or path is invalid" ), ) diff --git a/backend/utils/api.py b/backend/utils/api.py deleted file mode 100644 index 2ae7941..0000000 --- a/backend/utils/api.py +++ /dev/null @@ -1,31 +0,0 @@ -from pydantic import BaseModel -from typing import Any, Dict -from builtins import Exception as PyException -from enum import Enum - - -class Status(str, Enum): - success = "success" - failed = "failed" - - -class Error(BaseModel): - type: str - code: int - message: str | list[dict] - - -class Response(BaseModel): - status: Status - error: Error | None = None - data: Dict[str, Any] | BaseModel | None = None - - -class Exception(PyException): - status_code: int - error: Error - - def __init__(self, status_code: int, error: Error): - super().__init__(status_code) - self.status_code = status_code - self.error = error From 2bb07711102930c6129b9a49c6ccb3441ec67c90 Mon Sep 17 00:00:00 2001 From: splimterXrodina Date: Sun, 11 Sep 2022 22:35:52 +0100 Subject: [PATCH 3/3] fix circular depandancy --- backend/api/schemas/errors.py | 8 +++++++- backend/api/schemas/response.py | 20 +++++++------------- backend/main.py | 3 ++- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/backend/api/schemas/errors.py b/backend/api/schemas/errors.py index 38fd990..aa05267 100644 --- a/backend/api/schemas/errors.py +++ b/backend/api/schemas/errors.py @@ -3,7 +3,13 @@ # NOT_AUTHENTICATED = Error(err_type="auth", code=10, message="Not authenticated") -from backend.api.schemas.response import Error +from pydantic import BaseModel + + +class Error(BaseModel): + err_type: str + code: int + message: str | list[dict] EXPIRED_TOKEN = Error( diff --git a/backend/api/schemas/response.py b/backend/api/schemas/response.py index 8904d91..8f4c2fe 100644 --- a/backend/api/schemas/response.py +++ b/backend/api/schemas/response.py @@ -10,23 +10,17 @@ class Status(str, Enum): failed = "failed" -class Error(BaseModel): - err_type: str - code: int - message: str | list[dict] - - class APIResponse(BaseModel): status: Status - error: Error | None = None + error: api_errors.Error | None = None data: Dict[str, Any] | BaseModel | None = None class APIException(PyException): status_code: int - error: Error + error: api_errors.Error - def __init__(self, status_code: int, error: Error): + def __init__(self, status_code: int, error: api_errors.Error): super().__init__(status_code) self.status_code = status_code self.error = error @@ -34,19 +28,19 @@ def __init__(self, status_code: int, error: Error): class INVALID_ACCESS_TOKENResponse(APIResponse): status: Status = Status.failed - error: Error = api_errors.INVALID_ACCESS_TOKEN + error: api_errors.Error = api_errors.INVALID_ACCESS_TOKEN class ExpiredTokenResponse(APIResponse): status: Status = Status.failed - error: Error = api_errors.EXPIRED_TOKEN + error: api_errors.Error = api_errors.EXPIRED_TOKEN class ValidationErrorResponse(APIResponse): status: Status = Status.failed - error: Error = api_errors.VALIDATION_ERR + error: api_errors.Error = api_errors.VALIDATION_ERR class EligibilityErrorResponse(APIResponse): status: Status = Status.failed - error: Error = api_errors.ELIGIBILITY_ERR + error: api_errors.Error = api_errors.ELIGIBILITY_ERR diff --git a/backend/main.py b/backend/main.py index 241ffda..5541604 100644 --- a/backend/main.py +++ b/backend/main.py @@ -18,7 +18,8 @@ from hypercorn.config import Config from starlette.exceptions import HTTPException as StarletteHTTPException from api.schemas import examples as api_examples -from api.schemas.response import APIException, APIResponse, Error, Status +from api.schemas.response import APIException, APIResponse, Status +from api.schemas.errors import Error from utils.response import OurResponse from utils.logger import logger from utils.settings import settings