Compare commits

...

2 Commits

9 changed files with 156 additions and 19 deletions

View File

@ -1,7 +1,15 @@
from dataclasses import dataclass
from uuid import UUID
@dataclass(frozen=True)
class AddMenuDTO:
title: str
description: str | None
@dataclass(frozen=True)
class UpdateMenuDTO:
id: UUID
title: str | None
description: str | None

View File

@ -0,0 +1,24 @@
from fastfood_two.application.abstractions.interactor import Interactor
from fastfood_two.application.contracts.requests import UpdateMenuDTO
from fastfood_two.application.contracts.responses import MenuDTO
from fastfood_two.domain.menu.gateway import MenuGateway
from fastfood_two.domain.menu.menu_entity import Description, MenuId, Title
class UpdateMenu(Interactor[UpdateMenuDTO, MenuDTO]):
def __init__(self, gateway: MenuGateway) -> None:
self._menu_gateway = gateway
async def __call__(self, request: UpdateMenuDTO) -> MenuDTO:
menu = await self._menu_gateway.update_menu(
id=MenuId(request.id),
title=Title(request.title) if request.title else None,
description=Description(request.description) if request.description else None,
)
return MenuDTO(
id=menu.id.value,
title=menu.title.value,
description=menu.description.value,
)

View File

@ -1,7 +1,7 @@
from typing import Protocol
from uuid import UUID
from fastfood_two.domain.menu.menu_entity import Menu
from fastfood_two.domain.menu.menu_entity import Description, Menu, MenuId, Title
class MenuGateway(Protocol):
@ -14,7 +14,12 @@ class MenuGateway(Protocol):
async def get_all_menus(self) -> list[Menu]:
raise NotImplementedError
async def update_menu(self, updated_menu: Menu) -> Menu | None:
async def update_menu(
self,
id: MenuId,
title: Title | None,
description: Description | None,
) -> Menu:
raise NotImplementedError
async def delete_menu(self, id: UUID) -> None:

View File

@ -3,7 +3,7 @@ from uuid import UUID
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
from fastfood_two.domain.menu.menu_entity import Menu
from fastfood_two.domain.menu.menu_entity import Description, Menu, MenuId, Title
from fastfood_two.infrastructure.pg_storage.mappers.menu_mapper import (
db_entity_to_domain,
)
@ -16,12 +16,40 @@ class MenuGatewayImpl:
async def get_all_menus(self) -> list[Menu]:
query = text("SELECT * FROM menu;")
menus = await self._session.execute(query)
return [db_entity_to_domain(menu) for menu in menus.scalars().all()]
return [db_entity_to_domain(menu.tuple()) for menu in menus]
async def get_menu_by_id(self, id: UUID) -> Menu | None: ...
async def insert_menu(self, menu: Menu) -> Menu: ...
async def insert_menu(self, menu: Menu) -> Menu:
query = text("INSERT INTO menu (id, title, description) VALUES (:id, :title, :description);")
await self._session.execute(
query,
{
"id": menu.id.value,
"title": menu.title.value,
"description": menu.description.value,
},
)
await self._session.commit()
return menu
async def update_menu(self, updated_menu: Menu) -> Menu | None: ...
async def update_menu(self, id: MenuId, title: Title | None, description: Description | None) -> Menu:
query = text(
"UPDATE menu SET {}{} {} WHERE id = :id RETURNING *;".format(
"title = :title " if title is not None else "",
"," if all([title is not None, description is not None]) else "",
"description = :description" if description is not None else "",
)
)
menu = await self._session.execute(
query,
{
"id": id.value,
"title": title.value if title else None,
"description": description.value if description else None,
},
)
await self._session.commit()
return db_entity_to_domain(menu.one().tuple())
async def delete_menu(self, id: UUID) -> None: ...

View File

@ -1,14 +1,12 @@
from typing import TYPE_CHECKING
from uuid import UUID
from fastfood_two.domain.menu.menu_entity import Menu
if TYPE_CHECKING:
from fastfood_two.infrastructure.pg_storage.models import SQLAMenu
from fastfood_two.domain.menu.menu_entity import Description, Menu, MenuId, Title
def db_entity_to_domain(menu: "SQLAMenu") -> Menu:
def db_entity_to_domain(menu: tuple[UUID, str, str | None]) -> Menu:
print(menu)
return Menu(
id=menu.id,
title=menu.title,
description=menu.description,
id=MenuId(menu[0]),
title=Title(menu[1]),
description=Description(menu[2]),
)

View File

@ -5,7 +5,9 @@ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
from fastfood_two.application.common.logger import configure_logger
from fastfood_two.application.config import Config
from fastfood_two.application.usecases.menu.add_menu import AddMenu
from fastfood_two.application.usecases.menu.get_all_menus import GetAllMenus
from fastfood_two.application.usecases.menu.update_menu import UpdateMenu
from fastfood_two.domain.menu.gateway import MenuGateway
from fastfood_two.presentation.fastapi_backend.depends.config import get_settings
from fastfood_two.presentation.fastapi_backend.depends.gateways import get_menu_gateway
@ -15,7 +17,9 @@ from fastfood_two.presentation.fastapi_backend.depends.session import (
get_session,
)
from fastfood_two.presentation.fastapi_backend.depends.usecases import (
add_menu_usecase,
get_all_menus_usecase,
update_menu_usecase,
)
logger = logging.getLogger(__name__)
@ -38,5 +42,7 @@ def init_dependencies(app: FastAPI) -> None:
app.dependency_overrides[MenuGateway] = get_menu_gateway
app.dependency_overrides[GetAllMenus] = get_all_menus_usecase
app.dependency_overrides[AddMenu] = add_menu_usecase
app.dependency_overrides[UpdateMenu] = update_menu_usecase
logger.info("Dependencies initialized")

View File

@ -4,6 +4,7 @@ from fastapi import Depends
from fastfood_two.application.usecases.menu.add_menu import AddMenu
from fastfood_two.application.usecases.menu.get_all_menus import GetAllMenus
from fastfood_two.application.usecases.menu.update_menu import UpdateMenu
from fastfood_two.domain.menu.gateway import MenuGateway
from fastfood_two.presentation.fastapi_backend.depends.stub import Stub
@ -14,3 +15,7 @@ def get_all_menus_usecase(gateway: Annotated[MenuGateway, Depends(Stub(MenuGatew
def add_menu_usecase(gateway: Annotated[MenuGateway, Depends(Stub(MenuGateway))]) -> AddMenu:
return AddMenu(gateway=gateway)
def update_menu_usecase(gateway: Annotated[MenuGateway, Depends(Stub(MenuGateway))]) -> UpdateMenu:
return UpdateMenu(gateway=gateway)

View File

@ -4,3 +4,8 @@ from pydantic import BaseModel
class AddMenuPDModel(BaseModel):
title: str
description: str | None
class UpdateMenuPDModel(BaseModel):
title: str | None = None
description: str | None = None

View File

@ -1,13 +1,18 @@
from typing import Annotated
from uuid import UUID
from fastapi import APIRouter, Depends
from fastfood_two.application.contracts.requests import AddMenuDTO
from fastfood_two.application.contracts.requests import AddMenuDTO, UpdateMenuDTO
from fastfood_two.application.contracts.responses import MenuDTO
from fastfood_two.application.usecases.menu.add_menu import AddMenu
from fastfood_two.application.usecases.menu.get_all_menus import GetAllMenus
from fastfood_two.application.usecases.menu.update_menu import UpdateMenu
from fastfood_two.presentation.fastapi_backend.depends.stub import Stub
from fastfood_two.presentation.fastapi_backend.pdmodels.menu import AddMenuPDModel
from fastfood_two.presentation.fastapi_backend.pdmodels.menu import (
AddMenuPDModel,
UpdateMenuPDModel,
)
router = APIRouter(prefix="/menu", tags=["Menu"])
@ -19,6 +24,15 @@ async def get_all_menus(
"""Get all menus.
Endpoint returns list of all available food menus
Parameters
----------
None
Returns
-------
list[MenuDTO]
list of created menu items
"""
menus = await usecase()
return menus
@ -29,9 +43,53 @@ async def add_menu(
request: AddMenuPDModel,
usecase: Annotated[AddMenu, Depends(Stub(AddMenu))],
) -> MenuDTO:
"""Get all menus.
"""Add menus.
Endpoint returns list of all available food menus
Endpoint allows to add new food menu
Parameters
----------
title: str
title of the menu
description: str
description of the menu
Returns
-------
MenuDTO
created menu
"""
menus = await usecase(request=AddMenuDTO(title=request.title, description=request.description))
return menus
@router.patch("/{menu_id}", response_model=MenuDTO)
async def update_menu(
menu_id: UUID,
request: UpdateMenuPDModel,
usecase: Annotated[UpdateMenu, Depends(Stub(UpdateMenu))],
) -> MenuDTO:
"""Update menus.
Endpoint allows to update food menu
Parameters
----------
title: str | None
new title of the menu or None if you don't want to update it
description: str | None
new description of the menu or None if you don't want to update it
Returns
-------
MenuDTO
updated menu
"""
menus = await usecase(
request=UpdateMenuDTO(
id=menu_id,
title=request.title,
description=request.description,
)
)
return menus