Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions backend/api/schemas/data.py

This file was deleted.

22 changes: 15 additions & 7 deletions backend/api/schemas/errors.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
""" 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 pydantic import BaseModel


class Error(BaseModel):
err_type: str
code: int
message: str | list[dict]


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",
)
58 changes: 21 additions & 37 deletions backend/api/schemas/response.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,46 @@
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

@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: api_errors.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
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


class INVALID_ACCESS_TOKENResponse(ApiResponse):
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):
class ExpiredTokenResponse(APIResponse):
status: Status = Status.failed
error: Error = api_errors.EXPIRED_TOKEN
error: api_errors.Error = api_errors.EXPIRED_TOKEN


class ValidationErrorResponse(ApiResponse):
class ValidationErrorResponse(APIResponse):
status: Status = Status.failed
error: Error = api_errors.VALIDATION_ERR
error: api_errors.Error = api_errors.VALIDATION_ERR


class EligibilityErrorResponse(ApiResponse):
class EligibilityErrorResponse(APIResponse):
status: Status = Status.failed
error: Error = api_errors.ELIGIBILITY_ERR
error: api_errors.Error = api_errors.ELIGIBILITY_ERR
20 changes: 10 additions & 10 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +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.data import Error, Status
from api.schemas.response import ApiException, ApiResponse
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
Expand Down Expand Up @@ -90,9 +90,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),
)


Expand All @@ -113,11 +113,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 = [
Expand Down Expand Up @@ -157,10 +157,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."
),
)
),
Expand Down Expand Up @@ -249,10 +249,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"
),
)

Expand Down