ADD update_menu usecase, fix mapper

main
Сергей Ванюшкин 2024-09-04 23:50:50 +00:00
parent 2104f39e3b
commit 36627d409c
9 changed files with 137 additions and 10 deletions

View File

@ -1,7 +1,15 @@
from dataclasses import dataclass from dataclasses import dataclass
from uuid import UUID
@dataclass(frozen=True) @dataclass(frozen=True)
class AddMenuDTO: class AddMenuDTO:
title: str title: str
description: str | None 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 typing import Protocol
from uuid import UUID 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): class MenuGateway(Protocol):
@ -14,7 +14,12 @@ class MenuGateway(Protocol):
async def get_all_menus(self) -> list[Menu]: async def get_all_menus(self) -> list[Menu]:
raise NotImplementedError 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 raise NotImplementedError
async def delete_menu(self, id: UUID) -> None: async def delete_menu(self, id: UUID) -> None:

View File

@ -3,7 +3,7 @@ from uuid import UUID
from sqlalchemy import text from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession 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 ( from fastfood_two.infrastructure.pg_storage.mappers.menu_mapper import (
db_entity_to_domain, db_entity_to_domain,
) )
@ -16,7 +16,7 @@ class MenuGatewayImpl:
async def get_all_menus(self) -> list[Menu]: async def get_all_menus(self) -> list[Menu]:
query = text("SELECT * FROM menu;") query = text("SELECT * FROM menu;")
menus = await self._session.execute(query) menus = await self._session.execute(query)
return [db_entity_to_domain(menu) for menu in menus] return [db_entity_to_domain(menu.tuple()) for menu in menus]
async def get_menu_by_id(self, id: UUID) -> Menu | None: ... async def get_menu_by_id(self, id: UUID) -> Menu | None: ...
@ -33,6 +33,23 @@ class MenuGatewayImpl:
await self._session.commit() await self._session.commit()
return menu 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: ... async def delete_menu(self, id: UUID) -> None: ...

View File

@ -1,7 +1,9 @@
from uuid import UUID
from fastfood_two.domain.menu.menu_entity import Description, Menu, MenuId, Title from fastfood_two.domain.menu.menu_entity import Description, Menu, MenuId, Title
def db_entity_to_domain(menu) -> Menu: def db_entity_to_domain(menu: tuple[UUID, str, str | None]) -> Menu:
print(menu) print(menu)
return Menu( return Menu(
id=MenuId(menu[0]), id=MenuId(menu[0]),

View File

@ -7,6 +7,7 @@ from fastfood_two.application.common.logger import configure_logger
from fastfood_two.application.config import Config from fastfood_two.application.config import Config
from fastfood_two.application.usecases.menu.add_menu import AddMenu 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.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.domain.menu.gateway import MenuGateway
from fastfood_two.presentation.fastapi_backend.depends.config import get_settings from fastfood_two.presentation.fastapi_backend.depends.config import get_settings
from fastfood_two.presentation.fastapi_backend.depends.gateways import get_menu_gateway from fastfood_two.presentation.fastapi_backend.depends.gateways import get_menu_gateway
@ -18,6 +19,7 @@ from fastfood_two.presentation.fastapi_backend.depends.session import (
from fastfood_two.presentation.fastapi_backend.depends.usecases import ( from fastfood_two.presentation.fastapi_backend.depends.usecases import (
add_menu_usecase, add_menu_usecase,
get_all_menus_usecase, get_all_menus_usecase,
update_menu_usecase,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -41,5 +43,6 @@ def init_dependencies(app: FastAPI) -> None:
app.dependency_overrides[GetAllMenus] = get_all_menus_usecase app.dependency_overrides[GetAllMenus] = get_all_menus_usecase
app.dependency_overrides[AddMenu] = add_menu_usecase app.dependency_overrides[AddMenu] = add_menu_usecase
app.dependency_overrides[UpdateMenu] = update_menu_usecase
logger.info("Dependencies initialized") 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.add_menu import AddMenu
from fastfood_two.application.usecases.menu.get_all_menus import GetAllMenus 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.domain.menu.gateway import MenuGateway
from fastfood_two.presentation.fastapi_backend.depends.stub import Stub 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: def add_menu_usecase(gateway: Annotated[MenuGateway, Depends(Stub(MenuGateway))]) -> AddMenu:
return AddMenu(gateway=gateway) 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): class AddMenuPDModel(BaseModel):
title: str title: str
description: str | None 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 typing import Annotated
from uuid import UUID
from fastapi import APIRouter, Depends 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.contracts.responses import MenuDTO
from fastfood_two.application.usecases.menu.add_menu import AddMenu 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.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.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"]) router = APIRouter(prefix="/menu", tags=["Menu"])
@ -19,6 +24,15 @@ async def get_all_menus(
"""Get all menus. """Get all menus.
Endpoint returns list of all available food menus Endpoint returns list of all available food menus
Parameters
----------
None
Returns
-------
list[MenuDTO]
list of created menu items
""" """
menus = await usecase() menus = await usecase()
return menus return menus
@ -29,9 +43,53 @@ async def add_menu(
request: AddMenuPDModel, request: AddMenuPDModel,
usecase: Annotated[AddMenu, Depends(Stub(AddMenu))], usecase: Annotated[AddMenu, Depends(Stub(AddMenu))],
) -> MenuDTO: ) -> 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)) menus = await usecase(request=AddMenuDTO(title=request.title, description=request.description))
return menus 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