Skip to content

Commit

Permalink
feat: human-readable error responses
Browse files Browse the repository at this point in the history
  • Loading branch information
fivan999 committed Nov 3, 2024
1 parent 761b863 commit 743e6c6
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 28 deletions.
5 changes: 5 additions & 0 deletions src/api/schemes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from pydantic import BaseModel


class ErrorScheme(BaseModel):
detail: str
15 changes: 15 additions & 0 deletions src/modules/workshops/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from enum import Enum


class CheckInEnum(Enum):
SUCCESS = "Success"
ALREADY_CHECKED_IN = "You already checked in this workshop"
WORKSHOP_DOES_NOT_EXIST = "Workshop does not exist"
NO_PLACES = "No places available"
INVALID_TIME = "You can not check in this workshop now"
CHECK_IN_DOES_NOT_EXIST = "You did not check in this workshop"


class WorkshopEnum(Enum):
CREATED = "Created"
ALIAS_ALREADY_EXISTS = "Alias already exists"
27 changes: 16 additions & 11 deletions src/modules/workshops/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from sqlalchemy.ext.asyncio import AsyncSession

from src.modules.users.schemes import ViewUserScheme
from src.modules.workshops.enums import CheckInEnum, WorkshopEnum
from src.modules.workshops.schemes import CreateWorkshopScheme, ViewWorkshopScheme
from src.storages.sql.models.users import User
from src.storages.sql.models.workshops import CheckIn, Workshop
Expand All @@ -14,15 +15,16 @@ class SqlWorkshopRepository:
def __init__(self, session: AsyncSession) -> None:
self.session = session

async def create(self, workshop: CreateWorkshopScheme) -> ViewWorkshopScheme | None:
async def create(self, workshop: CreateWorkshopScheme) -> tuple[ViewWorkshopScheme | None, WorkshopEnum]:
to_insert = workshop.model_dump()
to_insert["remain_places"] = workshop.capacity
query = insert(Workshop).values(**to_insert).on_conflict_do_nothing().returning(Workshop)
workshop = await self.session.execute(query)
workshop = workshop.scalar()
await self.session.commit()
if workshop is not None:
return ViewWorkshopScheme.model_validate(workshop, from_attributes=True)
return ViewWorkshopScheme.model_validate(workshop, from_attributes=True), WorkshopEnum.CREATED
return None, WorkshopEnum.ALIAS_ALREADY_EXISTS

async def read_all(self) -> list[ViewWorkshopScheme]:
query = select(Workshop)
Expand Down Expand Up @@ -57,39 +59,42 @@ async def exists(self, workshop_id: int, user_id: int) -> bool:
result = result.scalar()
return bool(result)

async def create(self, workshop_id: int, user_id: int) -> bool:
async def create(self, workshop_id: int, user_id: int) -> CheckInEnum:
workshop = await self.workshops_repository.get_by_id(workshop_id)
if workshop is None or workshop.remain_places == 0:
return False
if workshop is None:
return CheckInEnum.WORKSHOP_DOES_NOT_EXIST

if workshop.remain_places == 0:
return CheckInEnum.NO_PLACES

exists = await self.exists(workshop_id, user_id)
if exists:
return False
return CheckInEnum.ALREADY_CHECKED_IN

time_now = datetime.datetime.now(tz=None)
registration_start_time = workshop.dtend.replace(hour=0, minute=0, second=0)

if time_now < registration_start_time or time_now > workshop.dtstart:
return False
return CheckInEnum.INVALID_TIME

request = insert(CheckIn).values(workshop_id=workshop_id, user_id=user_id)
await self.session.execute(request)
await self.workshops_repository.update_remain_places(workshop_id, -1)
await self.session.commit()

return True
return CheckInEnum.SUCCESS

async def delete(self, workshop_id: int, user_id: int) -> bool:
async def delete(self, workshop_id: int, user_id: int) -> CheckInEnum:
exists = await self.exists(workshop_id, user_id)
if not exists:
return False
return CheckInEnum.CHECK_IN_DOES_NOT_EXIST

request = delete(CheckIn).where(CheckIn.workshop_id == workshop_id, CheckIn.user_id == user_id)
await self.session.execute(request)
await self.workshops_repository.update_remain_places(workshop_id, 1)
await self.session.commit()

return True
return CheckInEnum.SUCCESS

async def get_check_inned_users_by_workshop_id(self, workshop_id: int) -> list[ViewUserScheme] | None:
workshop = await self.workshops_repository.get_by_id(workshop_id)
Expand Down
36 changes: 19 additions & 17 deletions src/modules/workshops/routes.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from fastapi import APIRouter, Response, status
from fastapi import APIRouter, HTTPException, Response, status

from src.api.dependencies import CurrentUserIdDep
from src.api.exceptions import IncorrectCredentialsException
from src.api.schemes import ErrorScheme
from src.modules.users.schemes import ViewUserScheme
from src.modules.workshops.dependencies import SqlCheckInRepositoryDep, SqlWorkshopRepositoryDep
from src.modules.workshops.enums import CheckInEnum, WorkshopEnum
from src.modules.workshops.schemes import CreateWorkshopScheme, ViewWorkshopScheme

router = APIRouter(
Expand Down Expand Up @@ -31,16 +33,16 @@ async def read_all_workshops(workshop_repository: SqlWorkshopRepositoryDep) -> l
status_code=status.HTTP_201_CREATED,
responses={
status.HTTP_201_CREATED: {"description": "Workshop successfully created"},
status.HTTP_400_BAD_REQUEST: {"description": "Creation failed"},
status.HTTP_400_BAD_REQUEST: {"description": "Creation failed", "model": ErrorScheme},
},
)
async def create_workshop(
user_id: CurrentUserIdDep, workshop_repository: SqlWorkshopRepositoryDep, workshop: CreateWorkshopScheme
) -> ViewWorkshopScheme:
workshop = await workshop_repository.create(workshop)
if workshop:
return workshop
return Response(status_code=status.HTTP_400_BAD_REQUEST)
workshop, create_status = await workshop_repository.create(workshop)
if create_status != WorkshopEnum.CREATED:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=create_status.value)
return workshop


@router.get(
Expand All @@ -61,33 +63,33 @@ async def get_my_check_ins(
status_code=status.HTTP_200_OK,
responses={
status.HTTP_200_OK: {"description": "Check-in successful"},
status.HTTP_400_BAD_REQUEST: {"description": "Check-in failed"},
status.HTTP_400_BAD_REQUEST: {"description": "Check-in failed", "model": ErrorScheme},
},
)
async def check_in_workshop(
workshop_id: int, user_id: CurrentUserIdDep, check_ins_repository: SqlCheckInRepositoryDep
) -> Response:
checked_in = await check_ins_repository.create(workshop_id, user_id)
if checked_in:
return Response(status_code=status.HTTP_200_OK)
return Response(status_code=status.HTTP_400_BAD_REQUEST)
check_in_status = await check_ins_repository.create(workshop_id, user_id)
if check_in_status != CheckInEnum.SUCCESS:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=check_in_status.value)
return Response(status_code=status.HTTP_200_OK)


@router.post(
"/check-out/{workshop_id}",
status_code=status.HTTP_200_OK,
responses={
status.HTTP_200_OK: {"description": "Check-out successful"},
status.HTTP_400_BAD_REQUEST: {"description": "Check-out failed"},
status.HTTP_200_OK: {"description": "Check-out successful", "model": None},
status.HTTP_400_BAD_REQUEST: {"description": "Check-out failed", "model": ErrorScheme},
},
)
async def check_out_workshop(
workshop_id: int, user_id: CurrentUserIdDep, check_ins_repository: SqlCheckInRepositoryDep
) -> Response:
deleted = await check_ins_repository.delete(workshop_id, user_id)
if deleted:
return Response(status_code=status.HTTP_200_OK)
return Response(status_code=status.HTTP_400_BAD_REQUEST)
delete_status = await check_ins_repository.delete(workshop_id, user_id)
if delete_status != CheckInEnum.SUCCESS:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=delete_status.value)
return Response(status_code=status.HTTP_200_OK)


@router.get(
Expand Down

0 comments on commit 743e6c6

Please sign in to comment.