From cbcebdffb5b28b49be50b150420b795e344080c3 Mon Sep 17 00:00:00 2001 From: Sergey Vanyushkin Date: Sun, 1 Sep 2024 12:22:47 +0000 Subject: [PATCH] UPDATE refactor project structure --- alembic.ini | 2 +- poetry.lock | 26 +++++----- src/fastfood_two/__main__.py | 2 +- src/fastfood_two/app/dependencies.py | 32 ------------ .../__init__.py => application/__init__py} | 0 .../abstractions}/__init__.py | 0 .../abstractions}/interactor.py | 0 .../{ => application}/common/logger.py | 0 src/fastfood_two/{ => application}/config.py | 2 +- .../contracts}/__init__.py | 0 .../application/contracts/gateways.py | 8 +++ .../contracts/requests.py} | 0 .../{ => application}/contracts/responses.py | 2 +- .../usecases}/__init__.py | 0 .../usecases/menu/get_all_menus.py | 12 +++++ .../{storage => domain}/__init__.py | 0 .../pg_storage => domain/core}/__init__.py | 0 .../{usecases => infrastructure}/__init__.py | 0 .../infrastructure/menu_gateway.py | 15 ++++++ .../pg_storage/__init__.py} | 0 .../pg_storage/config.py | 0 .../pg_storage/mappers/menu_mapper.py | 14 ++++++ .../pg_storage/migrations/README | 0 .../pg_storage/migrations/env.py | 3 ++ .../pg_storage/migrations/script.py.mako | 0 .../migrations/versions/b108cf5ea628_init.py | 49 +++++++++++++++++++ .../pg_storage/models/__init__.py | 4 -- .../pg_storage/models/base.py | 0 .../pg_storage/models/common_attrs.py | 0 .../pg_storage/models/dish_model.py | 5 +- .../pg_storage/models/menu_model.py | 22 +++++++++ src/fastfood_two/presentation/__init__.py | 0 .../presentation/fastapi_backend/__init__.py | 0 .../fastapi_backend/dependencies.py | 42 ++++++++++++++++ .../fastapi_backend/depends/__init__.py | 0 .../fastapi_backend}/depends/config.py | 4 +- .../fastapi_backend/depends/gateways.py | 12 +++++ .../fastapi_backend}/depends/session.py | 4 +- .../fastapi_backend/depends}/stub.py | 0 .../fastapi_backend/depends/usecases.py | 11 +++++ .../fastapi_backend}/error_handlers.py | 0 .../fastapi_backend}/main.py | 8 +-- .../fastapi_backend/routers/__init__.py | 0 .../fastapi_backend}/routers/dish.py | 0 .../fastapi_backend/routers/menu.py | 21 ++++++++ .../fastapi_backend}/routers/submenu.py | 0 .../fastapi_backend}/routers/summary.py | 0 .../fastapi_backend/routers_init.py} | 12 +++-- src/fastfood_two/routers/menu.py | 10 ---- .../storage/pg_storage/models/menu_model.py | 18 ------- .../pg_storage/models/submenu_model.py | 22 --------- .../usecases/menu/get_all_menus.py | 8 --- 52 files changed, 245 insertions(+), 125 deletions(-) delete mode 100644 src/fastfood_two/app/dependencies.py rename src/fastfood_two/{app/__init__.py => application/__init__py} (100%) rename src/fastfood_two/{app/depends => application/abstractions}/__init__.py (100%) rename src/fastfood_two/{common => application/abstractions}/interactor.py (100%) rename src/fastfood_two/{ => application}/common/logger.py (100%) rename src/fastfood_two/{ => application}/config.py (57%) rename src/fastfood_two/{common => application/contracts}/__init__.py (100%) create mode 100644 src/fastfood_two/application/contracts/gateways.py rename src/fastfood_two/{contracts/__init__.py => application/contracts/requests.py} (100%) rename src/fastfood_two/{ => application}/contracts/responses.py (90%) rename src/fastfood_two/{routers => application/usecases}/__init__.py (100%) create mode 100644 src/fastfood_two/application/usecases/menu/get_all_menus.py rename src/fastfood_two/{storage => domain}/__init__.py (100%) rename src/fastfood_two/{storage/pg_storage => domain/core}/__init__.py (100%) rename src/fastfood_two/{usecases => infrastructure}/__init__.py (100%) create mode 100644 src/fastfood_two/infrastructure/menu_gateway.py rename src/fastfood_two/{contracts/usecases.py => infrastructure/pg_storage/__init__.py} (100%) rename src/fastfood_two/{storage => infrastructure}/pg_storage/config.py (100%) create mode 100644 src/fastfood_two/infrastructure/pg_storage/mappers/menu_mapper.py rename src/fastfood_two/{storage => infrastructure}/pg_storage/migrations/README (100%) rename src/fastfood_two/{storage => infrastructure}/pg_storage/migrations/env.py (93%) rename src/fastfood_two/{storage => infrastructure}/pg_storage/migrations/script.py.mako (100%) create mode 100644 src/fastfood_two/infrastructure/pg_storage/migrations/versions/b108cf5ea628_init.py rename src/fastfood_two/{storage => infrastructure}/pg_storage/models/__init__.py (68%) rename src/fastfood_two/{storage => infrastructure}/pg_storage/models/base.py (100%) rename src/fastfood_two/{storage => infrastructure}/pg_storage/models/common_attrs.py (100%) rename src/fastfood_two/{storage => infrastructure}/pg_storage/models/dish_model.py (76%) create mode 100644 src/fastfood_two/infrastructure/pg_storage/models/menu_model.py create mode 100644 src/fastfood_two/presentation/__init__.py create mode 100644 src/fastfood_two/presentation/fastapi_backend/__init__.py create mode 100644 src/fastfood_two/presentation/fastapi_backend/dependencies.py create mode 100644 src/fastfood_two/presentation/fastapi_backend/depends/__init__.py rename src/fastfood_two/{app => presentation/fastapi_backend}/depends/config.py (79%) create mode 100644 src/fastfood_two/presentation/fastapi_backend/depends/gateways.py rename src/fastfood_two/{app => presentation/fastapi_backend}/depends/session.py (87%) rename src/fastfood_two/{common => presentation/fastapi_backend/depends}/stub.py (100%) create mode 100644 src/fastfood_two/presentation/fastapi_backend/depends/usecases.py rename src/fastfood_two/{app => presentation/fastapi_backend}/error_handlers.py (100%) rename src/fastfood_two/{app => presentation/fastapi_backend}/main.py (74%) create mode 100644 src/fastfood_two/presentation/fastapi_backend/routers/__init__.py rename src/fastfood_two/{ => presentation/fastapi_backend}/routers/dish.py (100%) create mode 100644 src/fastfood_two/presentation/fastapi_backend/routers/menu.py rename src/fastfood_two/{ => presentation/fastapi_backend}/routers/submenu.py (100%) rename src/fastfood_two/{ => presentation/fastapi_backend}/routers/summary.py (100%) rename src/fastfood_two/{app/routers.py => presentation/fastapi_backend/routers_init.py} (54%) delete mode 100644 src/fastfood_two/routers/menu.py delete mode 100644 src/fastfood_two/storage/pg_storage/models/menu_model.py delete mode 100644 src/fastfood_two/storage/pg_storage/models/submenu_model.py delete mode 100644 src/fastfood_two/usecases/menu/get_all_menus.py diff --git a/alembic.ini b/alembic.ini index 8facaa4..4a9bfb6 100644 --- a/alembic.ini +++ b/alembic.ini @@ -3,7 +3,7 @@ [alembic] # path to migration scripts # Use forward slashes (/) also on windows to provide an os agnostic path -script_location = ./src/fastfood_two/storage/pg_storage/migrations +script_location = ./src/fastfood_two/infrastructure/pg_storage/migrations # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s # Uncomment the line below if you want the files to be prepended with date and time diff --git a/poetry.lock b/poetry.lock index cbc717b..572574f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -283,23 +283,23 @@ files = [ [[package]] name = "fastapi" -version = "0.112.0" +version = "0.112.2" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.112.0-py3-none-any.whl", hash = "sha256:3487ded9778006a45834b8c816ec4a48d522e2631ca9e75ec5a774f1b052f821"}, - {file = "fastapi-0.112.0.tar.gz", hash = "sha256:d262bc56b7d101d1f4e8fc0ad2ac75bb9935fec504d2b7117686cec50710cf05"}, + {file = "fastapi-0.112.2-py3-none-any.whl", hash = "sha256:db84b470bd0e2b1075942231e90e3577e12a903c4dc8696f0d206a7904a7af1c"}, + {file = "fastapi-0.112.2.tar.gz", hash = "sha256:3d4729c038414d5193840706907a41839d839523da6ed0c2811f1168cac1798c"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.38.0" +starlette = ">=0.37.2,<0.39.0" typing-extensions = ">=4.8.0" [package.extras] -all = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] [[package]] name = "filelock" @@ -415,13 +415,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] @@ -1031,13 +1031,13 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "starlette" -version = "0.37.2" +version = "0.38.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, - {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, + {file = "starlette-0.38.2-py3-none-any.whl", hash = "sha256:4ec6a59df6bbafdab5f567754481657f7ed90dc9d69b0c9ff017907dd54faeff"}, + {file = "starlette-0.38.2.tar.gz", hash = "sha256:c7c0441065252160993a1a37cf2a73bb64d271b17303e0b0c1eb7191cfb12d75"}, ] [package.dependencies] diff --git a/src/fastfood_two/__main__.py b/src/fastfood_two/__main__.py index 5f28fa7..51c3a0c 100644 --- a/src/fastfood_two/__main__.py +++ b/src/fastfood_two/__main__.py @@ -3,7 +3,7 @@ import uvicorn def main(): uvicorn.run( - "fastfood_two.app.main:app_factory", + "fastfood_two.presentation.fastapi_backend.main:app_factory", host="0.0.0.0", port=8000, factory=True, diff --git a/src/fastfood_two/app/dependencies.py b/src/fastfood_two/app/dependencies.py deleted file mode 100644 index bc2088a..0000000 --- a/src/fastfood_two/app/dependencies.py +++ /dev/null @@ -1,32 +0,0 @@ -import logging - -from fastapi import FastAPI -from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker - -from fastfood_two.app.depends.config import get_settings -from fastfood_two.app.depends.session import ( - create_engine, - create_session_maker, - get_session, -) -from fastfood_two.common.logger import configure_logger -from fastfood_two.config import Config - -logger = logging.getLogger(__name__) -configure_logger(level=logging.INFO) - - -def init_dependencies(app: FastAPI) -> None: - """Initialize FastAPI dependencies. - - :param app: FastAPI application - :type app: FastAPI - """ - - app.dependency_overrides[Config] = get_settings - - app.dependency_overrides[AsyncEngine] = create_engine - app.dependency_overrides[async_sessionmaker[AsyncSession]] = create_session_maker - app.dependency_overrides[AsyncSession] = get_session - - logger.info("Dependencies initialized") diff --git a/src/fastfood_two/app/__init__.py b/src/fastfood_two/application/__init__py similarity index 100% rename from src/fastfood_two/app/__init__.py rename to src/fastfood_two/application/__init__py diff --git a/src/fastfood_two/app/depends/__init__.py b/src/fastfood_two/application/abstractions/__init__.py similarity index 100% rename from src/fastfood_two/app/depends/__init__.py rename to src/fastfood_two/application/abstractions/__init__.py diff --git a/src/fastfood_two/common/interactor.py b/src/fastfood_two/application/abstractions/interactor.py similarity index 100% rename from src/fastfood_two/common/interactor.py rename to src/fastfood_two/application/abstractions/interactor.py diff --git a/src/fastfood_two/common/logger.py b/src/fastfood_two/application/common/logger.py similarity index 100% rename from src/fastfood_two/common/logger.py rename to src/fastfood_two/application/common/logger.py diff --git a/src/fastfood_two/config.py b/src/fastfood_two/application/config.py similarity index 57% rename from src/fastfood_two/config.py rename to src/fastfood_two/application/config.py index 67a05cf..0530db2 100644 --- a/src/fastfood_two/config.py +++ b/src/fastfood_two/application/config.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from fastfood_two.storage.pg_storage.config import PostgresConfig +from fastfood_two.infrastructure.pg_storage.config import PostgresConfig @dataclass(frozen=True) diff --git a/src/fastfood_two/common/__init__.py b/src/fastfood_two/application/contracts/__init__.py similarity index 100% rename from src/fastfood_two/common/__init__.py rename to src/fastfood_two/application/contracts/__init__.py diff --git a/src/fastfood_two/application/contracts/gateways.py b/src/fastfood_two/application/contracts/gateways.py new file mode 100644 index 0000000..9fa6474 --- /dev/null +++ b/src/fastfood_two/application/contracts/gateways.py @@ -0,0 +1,8 @@ +from typing import Protocol + +from fastfood_two.application.contracts.responses import MenuResponse + + +class MenuGateway(Protocol): + async def get_all_menus(self) -> list[MenuResponse]: + raise NotImplementedError diff --git a/src/fastfood_two/contracts/__init__.py b/src/fastfood_two/application/contracts/requests.py similarity index 100% rename from src/fastfood_two/contracts/__init__.py rename to src/fastfood_two/application/contracts/requests.py diff --git a/src/fastfood_two/contracts/responses.py b/src/fastfood_two/application/contracts/responses.py similarity index 90% rename from src/fastfood_two/contracts/responses.py rename to src/fastfood_two/application/contracts/responses.py index 0e32a5f..e63050c 100644 --- a/src/fastfood_two/contracts/responses.py +++ b/src/fastfood_two/application/contracts/responses.py @@ -5,5 +5,5 @@ from uuid import UUID @dataclass(frozen=True) class MenuResponse: id: UUID - name: str + title: str description: str diff --git a/src/fastfood_two/routers/__init__.py b/src/fastfood_two/application/usecases/__init__.py similarity index 100% rename from src/fastfood_two/routers/__init__.py rename to src/fastfood_two/application/usecases/__init__.py diff --git a/src/fastfood_two/application/usecases/menu/get_all_menus.py b/src/fastfood_two/application/usecases/menu/get_all_menus.py new file mode 100644 index 0000000..38b66d1 --- /dev/null +++ b/src/fastfood_two/application/usecases/menu/get_all_menus.py @@ -0,0 +1,12 @@ +from fastfood_two.application.abstractions.interactor import Interactor +from fastfood_two.application.contracts.gateways import MenuGateway +from fastfood_two.application.contracts.responses import MenuResponse + + +class GetAllMenus(Interactor[None, list[MenuResponse]]): + def __init__(self, gateway: MenuGateway) -> None: + self._menu_gateway = gateway + + async def __call__(self, request=None) -> list[MenuResponse]: + menus = await self._menu_gateway.get_all_menus() + return menus diff --git a/src/fastfood_two/storage/__init__.py b/src/fastfood_two/domain/__init__.py similarity index 100% rename from src/fastfood_two/storage/__init__.py rename to src/fastfood_two/domain/__init__.py diff --git a/src/fastfood_two/storage/pg_storage/__init__.py b/src/fastfood_two/domain/core/__init__.py similarity index 100% rename from src/fastfood_two/storage/pg_storage/__init__.py rename to src/fastfood_two/domain/core/__init__.py diff --git a/src/fastfood_two/usecases/__init__.py b/src/fastfood_two/infrastructure/__init__.py similarity index 100% rename from src/fastfood_two/usecases/__init__.py rename to src/fastfood_two/infrastructure/__init__.py diff --git a/src/fastfood_two/infrastructure/menu_gateway.py b/src/fastfood_two/infrastructure/menu_gateway.py new file mode 100644 index 0000000..632c717 --- /dev/null +++ b/src/fastfood_two/infrastructure/menu_gateway.py @@ -0,0 +1,15 @@ +from sqlalchemy import text +from sqlalchemy.ext.asyncio import AsyncSession + +from fastfood_two.application.contracts.responses import MenuResponse +from fastfood_two.infrastructure.pg_storage.mappers.menu_mapper import entity_to_dto + + +class MenuGatewayImpl: + def __init__(self, session: AsyncSession) -> None: + self._session = session + + async def get_all_menus(self) -> list[MenuResponse]: + query = text("SELECT * FROM menu;") + menus = await self._session.execute(query) + return [entity_to_dto(menu) for menu in menus.scalars().all()] diff --git a/src/fastfood_two/contracts/usecases.py b/src/fastfood_two/infrastructure/pg_storage/__init__.py similarity index 100% rename from src/fastfood_two/contracts/usecases.py rename to src/fastfood_two/infrastructure/pg_storage/__init__.py diff --git a/src/fastfood_two/storage/pg_storage/config.py b/src/fastfood_two/infrastructure/pg_storage/config.py similarity index 100% rename from src/fastfood_two/storage/pg_storage/config.py rename to src/fastfood_two/infrastructure/pg_storage/config.py diff --git a/src/fastfood_two/infrastructure/pg_storage/mappers/menu_mapper.py b/src/fastfood_two/infrastructure/pg_storage/mappers/menu_mapper.py new file mode 100644 index 0000000..0ea1fd8 --- /dev/null +++ b/src/fastfood_two/infrastructure/pg_storage/mappers/menu_mapper.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING + +from fastfood_two.application.contracts.responses import MenuResponse + +if TYPE_CHECKING: + from fastfood_two.infrastructure.pg_storage.models import Menu + + +def entity_to_dto(menu: "Menu") -> MenuResponse: + return MenuResponse( + id=menu.id, + title=menu.title, + description=menu.description or "", + ) diff --git a/src/fastfood_two/storage/pg_storage/migrations/README b/src/fastfood_two/infrastructure/pg_storage/migrations/README similarity index 100% rename from src/fastfood_two/storage/pg_storage/migrations/README rename to src/fastfood_two/infrastructure/pg_storage/migrations/README diff --git a/src/fastfood_two/storage/pg_storage/migrations/env.py b/src/fastfood_two/infrastructure/pg_storage/migrations/env.py similarity index 93% rename from src/fastfood_two/storage/pg_storage/migrations/env.py rename to src/fastfood_two/infrastructure/pg_storage/migrations/env.py index c0dd2a5..0a1730f 100644 --- a/src/fastfood_two/storage/pg_storage/migrations/env.py +++ b/src/fastfood_two/infrastructure/pg_storage/migrations/env.py @@ -3,12 +3,15 @@ from logging.config import fileConfig from alembic import context from sqlalchemy import engine_from_config, pool +from fastfood_two.app.depends.config import get_settings from fastfood_two.storage.pg_storage.models import Base # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config +config.set_main_option("sqlalchemy.url", f"{get_settings().db.url}?async_fallback=True") + # Interpret the config file for Python logging. # This line sets up loggers basically. if config.config_file_name is not None: diff --git a/src/fastfood_two/storage/pg_storage/migrations/script.py.mako b/src/fastfood_two/infrastructure/pg_storage/migrations/script.py.mako similarity index 100% rename from src/fastfood_two/storage/pg_storage/migrations/script.py.mako rename to src/fastfood_two/infrastructure/pg_storage/migrations/script.py.mako diff --git a/src/fastfood_two/infrastructure/pg_storage/migrations/versions/b108cf5ea628_init.py b/src/fastfood_two/infrastructure/pg_storage/migrations/versions/b108cf5ea628_init.py new file mode 100644 index 0000000..a01bccf --- /dev/null +++ b/src/fastfood_two/infrastructure/pg_storage/migrations/versions/b108cf5ea628_init.py @@ -0,0 +1,49 @@ +"""init + +Revision ID: b108cf5ea628 +Revises: +Create Date: 2024-08-31 06:45:06.377821 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "b108cf5ea628" +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "menu", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("title", sa.String(), nullable=False), + sa.Column("description", sa.String(), nullable=True), + sa.Column("parent", sa.UUID(), nullable=True), + sa.ForeignKeyConstraint(["parent"], ["menu.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_table( + "dish", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("title", sa.String(), nullable=False), + sa.Column("description", sa.String(), nullable=True), + sa.Column("price", sa.Float(), nullable=False), + sa.Column("parent_submenu", sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(["parent_submenu"], ["menu.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("dish") + op.drop_table("menu") + # ### end Alembic commands ### diff --git a/src/fastfood_two/storage/pg_storage/models/__init__.py b/src/fastfood_two/infrastructure/pg_storage/models/__init__.py similarity index 68% rename from src/fastfood_two/storage/pg_storage/models/__init__.py rename to src/fastfood_two/infrastructure/pg_storage/models/__init__.py index 50b7e0a..392b292 100644 --- a/src/fastfood_two/storage/pg_storage/models/__init__.py +++ b/src/fastfood_two/infrastructure/pg_storage/models/__init__.py @@ -1,14 +1,10 @@ from .base import Base from .menu_model import Menu -from .submenu_model import SubMenu from .dish_model import Dish from .common_attrs import uuidpk, str_25 __all__ = [ "Base", "Menu", - "SubMenu", "Dish", - "uuidpk", - "str_25", ] diff --git a/src/fastfood_two/storage/pg_storage/models/base.py b/src/fastfood_two/infrastructure/pg_storage/models/base.py similarity index 100% rename from src/fastfood_two/storage/pg_storage/models/base.py rename to src/fastfood_two/infrastructure/pg_storage/models/base.py diff --git a/src/fastfood_two/storage/pg_storage/models/common_attrs.py b/src/fastfood_two/infrastructure/pg_storage/models/common_attrs.py similarity index 100% rename from src/fastfood_two/storage/pg_storage/models/common_attrs.py rename to src/fastfood_two/infrastructure/pg_storage/models/common_attrs.py diff --git a/src/fastfood_two/storage/pg_storage/models/dish_model.py b/src/fastfood_two/infrastructure/pg_storage/models/dish_model.py similarity index 76% rename from src/fastfood_two/storage/pg_storage/models/dish_model.py rename to src/fastfood_two/infrastructure/pg_storage/models/dish_model.py index 844a69c..8de56f8 100644 --- a/src/fastfood_two/storage/pg_storage/models/dish_model.py +++ b/src/fastfood_two/infrastructure/pg_storage/models/dish_model.py @@ -3,7 +3,8 @@ import uuid from sqlalchemy import ForeignKey from sqlalchemy.orm import Mapped, mapped_column -from . import Base, str_25, uuidpk +from .base import Base +from .common_attrs import str_25, uuidpk class Dish(Base): @@ -14,4 +15,4 @@ class Dish(Base): description: Mapped[str | None] price: Mapped[float] - parent_submenu: Mapped[uuid.UUID] = mapped_column(ForeignKey("submenu.id", ondelete="CASCADE")) + parent_submenu: Mapped[uuid.UUID] = mapped_column(ForeignKey("menu.id", ondelete="CASCADE")) diff --git a/src/fastfood_two/infrastructure/pg_storage/models/menu_model.py b/src/fastfood_two/infrastructure/pg_storage/models/menu_model.py new file mode 100644 index 0000000..013a47c --- /dev/null +++ b/src/fastfood_two/infrastructure/pg_storage/models/menu_model.py @@ -0,0 +1,22 @@ +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from .base import Base +from .common_attrs import str_25, uuidpk + + +class Menu(Base): + __tablename__ = "menu" + + id: Mapped[uuidpk] + title: Mapped[str_25] + description: Mapped[str | None] + + parent: Mapped[uuidpk | None] = mapped_column(ForeignKey("menu.id", ondelete="CASCADE"), nullable=True) + + submenus: Mapped[list["Menu"]] = relationship( + "Menu", + backref="menu", + lazy="selectin", + cascade="all, delete", + ) diff --git a/src/fastfood_two/presentation/__init__.py b/src/fastfood_two/presentation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/fastfood_two/presentation/fastapi_backend/__init__.py b/src/fastfood_two/presentation/fastapi_backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/fastfood_two/presentation/fastapi_backend/dependencies.py b/src/fastfood_two/presentation/fastapi_backend/dependencies.py new file mode 100644 index 0000000..a339329 --- /dev/null +++ b/src/fastfood_two/presentation/fastapi_backend/dependencies.py @@ -0,0 +1,42 @@ +import logging + +from fastapi import FastAPI +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.contracts.gateways import MenuGateway +from fastfood_two.application.usecases.menu.get_all_menus import GetAllMenus +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.session import ( + create_engine, + create_session_maker, + get_session, +) +from fastfood_two.presentation.fastapi_backend.depends.usecases import ( + get_all_menus_usecase, +) + +logger = logging.getLogger(__name__) +configure_logger(level=logging.INFO) + + +def init_dependencies(app: FastAPI) -> None: + """Initialize FastAPI dependencies. + + :param app: FastAPI application + :type app: FastAPI + """ + + app.dependency_overrides[Config] = get_settings + + app.dependency_overrides[AsyncEngine] = create_engine + app.dependency_overrides[async_sessionmaker[AsyncSession]] = create_session_maker + app.dependency_overrides[AsyncSession] = get_session + + app.dependency_overrides[MenuGateway] = get_menu_gateway + + app.dependency_overrides[GetAllMenus] = get_all_menus_usecase + + logger.info("Dependencies initialized") diff --git a/src/fastfood_two/presentation/fastapi_backend/depends/__init__.py b/src/fastfood_two/presentation/fastapi_backend/depends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/fastfood_two/app/depends/config.py b/src/fastfood_two/presentation/fastapi_backend/depends/config.py similarity index 79% rename from src/fastfood_two/app/depends/config.py rename to src/fastfood_two/presentation/fastapi_backend/depends/config.py index ca21736..9df8f8c 100644 --- a/src/fastfood_two/app/depends/config.py +++ b/src/fastfood_two/presentation/fastapi_backend/depends/config.py @@ -1,8 +1,8 @@ import os from functools import lru_cache -from fastfood_two.config import Config -from fastfood_two.storage.pg_storage.config import PostgresConfig +from fastfood_two.application.config import Config +from fastfood_two.infrastructure.pg_storage.config import PostgresConfig @lru_cache() diff --git a/src/fastfood_two/presentation/fastapi_backend/depends/gateways.py b/src/fastfood_two/presentation/fastapi_backend/depends/gateways.py new file mode 100644 index 0000000..2620a42 --- /dev/null +++ b/src/fastfood_two/presentation/fastapi_backend/depends/gateways.py @@ -0,0 +1,12 @@ +from typing import Annotated + +from fastapi import Depends +from sqlalchemy.ext.asyncio import AsyncSession + +from fastfood_two.application.contracts.gateways import MenuGateway +from fastfood_two.infrastructure.menu_gateway import MenuGatewayImpl +from fastfood_two.presentation.fastapi_backend.depends.stub import Stub + + +def get_menu_gateway(session: Annotated[AsyncSession, Depends(Stub(AsyncSession))]) -> MenuGateway: + return MenuGatewayImpl(session=session) diff --git a/src/fastfood_two/app/depends/session.py b/src/fastfood_two/presentation/fastapi_backend/depends/session.py similarity index 87% rename from src/fastfood_two/app/depends/session.py rename to src/fastfood_two/presentation/fastapi_backend/depends/session.py index eca21e0..ea0bc23 100644 --- a/src/fastfood_two/app/depends/session.py +++ b/src/fastfood_two/presentation/fastapi_backend/depends/session.py @@ -10,8 +10,8 @@ from sqlalchemy.ext.asyncio import ( create_async_engine, ) -from fastfood_two.common.stub import Stub -from fastfood_two.config import Config +from fastfood_two.application.config import Config +from fastfood_two.presentation.fastapi_backend.depends.stub import Stub @lru_cache() diff --git a/src/fastfood_two/common/stub.py b/src/fastfood_two/presentation/fastapi_backend/depends/stub.py similarity index 100% rename from src/fastfood_two/common/stub.py rename to src/fastfood_two/presentation/fastapi_backend/depends/stub.py diff --git a/src/fastfood_two/presentation/fastapi_backend/depends/usecases.py b/src/fastfood_two/presentation/fastapi_backend/depends/usecases.py new file mode 100644 index 0000000..2cfacd8 --- /dev/null +++ b/src/fastfood_two/presentation/fastapi_backend/depends/usecases.py @@ -0,0 +1,11 @@ +from typing import Annotated + +from fastapi import Depends + +from fastfood_two.application.contracts.gateways import MenuGateway +from fastfood_two.application.usecases.menu.get_all_menus import GetAllMenus +from fastfood_two.presentation.fastapi_backend.depends.stub import Stub + + +def get_all_menus_usecase(gateway: Annotated[MenuGateway, Depends(Stub(MenuGateway))]) -> GetAllMenus: + return GetAllMenus(gateway=gateway) diff --git a/src/fastfood_two/app/error_handlers.py b/src/fastfood_two/presentation/fastapi_backend/error_handlers.py similarity index 100% rename from src/fastfood_two/app/error_handlers.py rename to src/fastfood_two/presentation/fastapi_backend/error_handlers.py diff --git a/src/fastfood_two/app/main.py b/src/fastfood_two/presentation/fastapi_backend/main.py similarity index 74% rename from src/fastfood_two/app/main.py rename to src/fastfood_two/presentation/fastapi_backend/main.py index 0a67faf..14b241e 100644 --- a/src/fastfood_two/app/main.py +++ b/src/fastfood_two/presentation/fastapi_backend/main.py @@ -4,10 +4,10 @@ from typing import AsyncGenerator from fastapi import FastAPI -from fastfood_two.app.dependencies import init_dependencies -from fastfood_two.app.error_handlers import init_errorhandlers -from fastfood_two.app.routers import init_routers -from fastfood_two.common.logger import configure_logger +from fastfood_two.application.common.logger import configure_logger +from fastfood_two.presentation.fastapi_backend.dependencies import init_dependencies +from fastfood_two.presentation.fastapi_backend.error_handlers import init_errorhandlers +from fastfood_two.presentation.fastapi_backend.routers_init import init_routers logger = logging.getLogger(__name__) configure_logger(level=logging.INFO) diff --git a/src/fastfood_two/presentation/fastapi_backend/routers/__init__.py b/src/fastfood_two/presentation/fastapi_backend/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/fastfood_two/routers/dish.py b/src/fastfood_two/presentation/fastapi_backend/routers/dish.py similarity index 100% rename from src/fastfood_two/routers/dish.py rename to src/fastfood_two/presentation/fastapi_backend/routers/dish.py diff --git a/src/fastfood_two/presentation/fastapi_backend/routers/menu.py b/src/fastfood_two/presentation/fastapi_backend/routers/menu.py new file mode 100644 index 0000000..0843c10 --- /dev/null +++ b/src/fastfood_two/presentation/fastapi_backend/routers/menu.py @@ -0,0 +1,21 @@ +from typing import Annotated + +from fastapi import APIRouter, Depends + +from fastfood_two.application.contracts.responses import MenuResponse +from fastfood_two.application.usecases.menu.get_all_menus import GetAllMenus +from fastfood_two.presentation.fastapi_backend.depends.stub import Stub + +router = APIRouter(prefix="/menu", tags=["Menu"]) + + +@router.get("/", response_model=list[MenuResponse]) +async def get_all_menus( + usecase: Annotated[GetAllMenus, Depends(Stub(GetAllMenus))], +) -> list[MenuResponse]: + """Get all menus. + + Endpoint returns list of all available food menus + """ + menus = await usecase() + return menus diff --git a/src/fastfood_two/routers/submenu.py b/src/fastfood_two/presentation/fastapi_backend/routers/submenu.py similarity index 100% rename from src/fastfood_two/routers/submenu.py rename to src/fastfood_two/presentation/fastapi_backend/routers/submenu.py diff --git a/src/fastfood_two/routers/summary.py b/src/fastfood_two/presentation/fastapi_backend/routers/summary.py similarity index 100% rename from src/fastfood_two/routers/summary.py rename to src/fastfood_two/presentation/fastapi_backend/routers/summary.py diff --git a/src/fastfood_two/app/routers.py b/src/fastfood_two/presentation/fastapi_backend/routers_init.py similarity index 54% rename from src/fastfood_two/app/routers.py rename to src/fastfood_two/presentation/fastapi_backend/routers_init.py index d9620d7..e522410 100644 --- a/src/fastfood_two/app/routers.py +++ b/src/fastfood_two/presentation/fastapi_backend/routers_init.py @@ -1,9 +1,13 @@ from fastapi import APIRouter, FastAPI -from fastfood_two.routers.dish import router as dish_router -from fastfood_two.routers.menu import router as menu_router -from fastfood_two.routers.submenu import router as submenu_router -from fastfood_two.routers.summary import router as summary_router +from fastfood_two.presentation.fastapi_backend.routers.dish import router as dish_router +from fastfood_two.presentation.fastapi_backend.routers.menu import router as menu_router +from fastfood_two.presentation.fastapi_backend.routers.submenu import ( + router as submenu_router, +) +from fastfood_two.presentation.fastapi_backend.routers.summary import ( + router as summary_router, +) def init_routers(app: FastAPI) -> None: diff --git a/src/fastfood_two/routers/menu.py b/src/fastfood_two/routers/menu.py deleted file mode 100644 index 4a08f79..0000000 --- a/src/fastfood_two/routers/menu.py +++ /dev/null @@ -1,10 +0,0 @@ -from fastapi import APIRouter - -from fastfood_two.contracts.responses import MenuResponse - -router = APIRouter(prefix="/menu", tags=["Menu"]) - - -@router.get("/", response_model=list[MenuResponse]) -async def get_all_menus() -> list[MenuResponse]: - return [] diff --git a/src/fastfood_two/storage/pg_storage/models/menu_model.py b/src/fastfood_two/storage/pg_storage/models/menu_model.py deleted file mode 100644 index cdbd4f4..0000000 --- a/src/fastfood_two/storage/pg_storage/models/menu_model.py +++ /dev/null @@ -1,18 +0,0 @@ -from sqlalchemy.orm import Mapped, relationship - -from . import Base, str_25, uuidpk - - -class Menu(Base): - __tablename__ = "menu" - - id: Mapped[uuidpk] - title: Mapped[str_25] - description: Mapped[str | None] - - submenus: Mapped[list["SubMenu"]] = relationship( - "SubMenu", - backref="menu", - lazy="selectin", - cascade="all, delete", - ) diff --git a/src/fastfood_two/storage/pg_storage/models/submenu_model.py b/src/fastfood_two/storage/pg_storage/models/submenu_model.py deleted file mode 100644 index 6313259..0000000 --- a/src/fastfood_two/storage/pg_storage/models/submenu_model.py +++ /dev/null @@ -1,22 +0,0 @@ -import uuid - -from sqlalchemy.orm import Mapped, mapped_column, relationship -from sqlalchemy.orm.properties import ForeignKey - -from . import Base, str_25, uuidpk - - -class SubMenu(Base): - __tablename__ = "submenu" - - id: Mapped[uuidpk] - title: Mapped[str_25] - description: Mapped[str | None] - - parent_menu: Mapped[uuid.UUID] = mapped_column(ForeignKey("menu.id", ondelete="CASCADE")) - dishes: Mapped[list["Dish"]] = relationship( - "Dish", - backref="submenu", - lazy="selectin", - cascade="all, delete", - ) diff --git a/src/fastfood_two/usecases/menu/get_all_menus.py b/src/fastfood_two/usecases/menu/get_all_menus.py deleted file mode 100644 index 1c8d540..0000000 --- a/src/fastfood_two/usecases/menu/get_all_menus.py +++ /dev/null @@ -1,8 +0,0 @@ -from fastfood_two.common.interactor import Interactor -from fastfood_two.contracts.menu_contracts import MenusResponse - - -class GetAllMenus(Interactor[None, MenusResponse]): - def __init__(self) -> None: ... - - async def __call__(self, request=None) -> MenusResponse: ...