StartFromBegining

main
Сергей Ванюшкин 2024-03-31 00:58:43 +03:00
parent 08e6aa5cb1
commit b733a6bf9a
24 changed files with 43 additions and 296 deletions

View File

@ -1,46 +0,0 @@
from collections.abc import AsyncIterable
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
from api.depends import ( # new_unit_of_work,; new_user_repository,; new_user_service,
create_engine,
create_session_maker,
new_session,
)
from api.models import BaseModel
async def lifespan(app: FastAPI) -> AsyncIterable[None]:
engine = app.dependency_overrides[AsyncEngine]()
async with engine.begin() as conn:
await conn.run_sync(BaseModel.metadata.create_all)
yield
async with engine.begin() as conn:
await conn.run_sync(BaseModel.metadata.drop_all)
def init_dependencies(app: FastAPI) -> None:
app.dependency_overrides[AsyncEngine] = create_engine
app.dependency_overrides[async_sessionmaker[AsyncSession]] = create_session_maker
app.dependency_overrides[AsyncSession] = new_session
# app.dependency_overrides[UserRepository] = new_user_repository
# app.dependency_overrides[UnitOfWork] = new_unit_of_work
# app.dependency_overrides[UserService] = new_user_service
def create_app() -> FastAPI:
app = FastAPI(
lifespan=lifespan,
)
# app.include_router(user_router)
init_dependencies(app)
return app
app = create_app()

View File

@ -0,0 +1,3 @@
from .main import app_factory
__all__ = ("app_factory",)

9
api/app_builder/main.py Normal file
View File

@ -0,0 +1,9 @@
from fastapi import FastAPI
from .routers import init_routers
def app_factory() -> FastAPI:
app = FastAPI()
init_routers(app)
return app

View File

@ -0,0 +1,7 @@
from fastapi import FastAPI
from api.presentation.routers import healthcheck_router
def init_routers(app: FastAPI) -> None:
app.include_router(healthcheck_router)

View File

@ -0,0 +1,3 @@
from .ping_response import PingResponse
__all__ = ("PingResponse",)

View File

@ -2,6 +2,5 @@ from dataclasses import dataclass
@dataclass(frozen=True)
class TokenDTO:
access_token: str
token_type: str
class PingResponse:
status: str

View File

@ -1,47 +0,0 @@
import os
from dataclasses import dataclass
from functools import lru_cache
import yaml # type: ignore
@dataclass(frozen=True)
class DBSettings:
pg_user: str
pg_pass: str
pg_host: str
pg_port: int
pg_db: str
@property
def db_url(self) -> str:
return "postgresql+asyncpg://{}:{}@{}:{}/{}".format(
self.pg_user,
self.pg_pass,
self.pg_host,
self.pg_port,
self.pg_db,
)
@dataclass(frozen=True)
class RedisSettings:
redis_host: str
redis_port: int
@dataclass(frozen=True)
class Settings:
db: DBSettings
redis: RedisSettings
@lru_cache
def get_settings():
with open(os.getenv("CONFIG_PATH", "./config/api_config.yml")) as f:
config_data: dict = yaml.safe_load(f)
return Settings(
db=DBSettings(**config_data["db"]),
redis=RedisSettings(**config_data["redis"]),
)

View File

@ -1,92 +0,0 @@
from collections.abc import AsyncIterable, Callable
from typing import Annotated
from fastapi import Depends
from sqlalchemy.ext.asyncio import (
AsyncEngine,
AsyncSession,
async_sessionmaker,
create_async_engine,
)
from api.config import get_settings
class Stub:
"""
This class is used to prevent fastapi from digging into
real dependencies attributes detecting them as request data
So instead of
`interactor: Annotated[Interactor, Depends()]`
Write
`interactor: Annotated[Interactor, Depends(Stub(Interactor))]`
And then you can declare how to create it:
`app.dependency_overrids[Interactor] = some_real_factory`
"""
def __init__(self, dependency: Callable, **kwargs):
self._dependency = dependency
self._kwargs = kwargs
def __call__(self):
raise NotImplementedError
def __eq__(self, other) -> bool:
if isinstance(other, Stub):
return self._dependency == other._dependency and self._kwargs == other._kwargs
else:
if not self._kwargs:
return self._dependency == other
return False
def __hash__(self):
if not self._kwargs:
return hash(self._dependency)
serial = (
self._dependency,
*self._kwargs.items(),
)
return hash(serial)
# def new_user_repository(
# session: Annotated[AsyncSession, Depends(Stub(AsyncSession))],
# ) -> UserRepository:
# return SqlalchemyUserRepository(session)
#
#
# def new_unit_of_work(
# session: Annotated[AsyncSession, Depends(Stub(AsyncSession))],
# ) -> UnitOfWork:
# return SqlalchemyUnitOfWork(session)
#
def create_engine() -> AsyncEngine:
return create_async_engine(url=get_settings().db.db_url)
def create_session_maker(
engine: Annotated[AsyncEngine, Depends(Stub(AsyncEngine))],
) -> async_sessionmaker[AsyncSession]:
return async_sessionmaker(engine, expire_on_commit=False)
async def new_session(
session_maker: Annotated[
async_sessionmaker[AsyncSession],
Depends(Stub(async_sessionmaker[AsyncSession])),
],
) -> AsyncIterable[AsyncSession]:
async with session_maker() as session:
yield session
# def new_user_service(
# uow: Annotated[UnitOfWork, Depends()],
# user_repository: Annotated[UserRepository, Depends()],
# ) -> UserService:
# return UserService(uow, user_repository)

View File

@ -1,9 +0,0 @@
from .base_model import BaseModel
from .refers_model import RefersModel
from .user_model import UserModel
__all__ = (
"BaseModel",
"UserModel",
"RefersModel",
)

View File

@ -1,13 +0,0 @@
import uuid
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class BaseModel(DeclarativeBase):
__abstract__ = True
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
primary_key=True,
)

View File

@ -1,18 +0,0 @@
from uuid import UUID
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from api.models import BaseModel
class RefersModel(BaseModel):
__tablename__ = "refers"
referer: Mapped[UUID] = mapped_column(ForeignKey("user.id"))
referals: Mapped[list["UserModel"]] = relationship(
"UserModel",
backref="refers",
lazy="selectin",
)
is_active: Mapped[bool]

View File

@ -1,11 +0,0 @@
from sqlalchemy.orm import Mapped, mapped_column
from api.models import BaseModel
class UserModel(BaseModel):
__tablename__ = "user"
name: Mapped[str]
email: Mapped[str] = mapped_column(unique=True)
hashed_password: Mapped[str]

View File

@ -0,0 +1,3 @@
from .ping import healthcheck_router
__all__ = ("healthcheck_router",)

View File

@ -0,0 +1,13 @@
from fastapi import APIRouter
from api.application.contracts.healht_check import PingResponse
healthcheck_router = APIRouter(
prefix="/ping",
tags=["HealthCheck"],
)
@healthcheck_router.get("/", status_code=200, response_model=PingResponse)
async def ping_pong() -> PingResponse:
return PingResponse(status="System OK")

View File

@ -1,28 +0,0 @@
from dataclasses import dataclass
from datetime import datetime
from uuid import UUID
@dataclass(frozen=True)
class LifetimeRequestDTO:
days: int = 0
hours: int = 0
minutes: int = 30
@dataclass(frozen=True)
class ReferRequestDTO:
lifetime: LifetimeRequestDTO
@dataclass(frozen=True)
class RefererResponseDTO:
name: str
email: str
@dataclass(frozen=True)
class ReferResponseDTO:
refer_id: UUID
expire_at: datetime
referer: RefererResponseDTO

View File

@ -1,16 +0,0 @@
from dataclasses import dataclass
from uuid import UUID
@dataclass(frozen=True)
class UserRequestDTO:
name: str
email: str
password: str
@dataclass(frozen=True)
class UserResponseDTO:
id: UUID
name: str
email: str

View File

@ -1,3 +0,0 @@
from .user import UserService
__all__ = ("UserService",)

View File

View File

@ -42,9 +42,9 @@ services:
volumes:
- ./config/api_config.yml:/usr/src/service_man/config/api_config.yml
- ./api:/usr/src/service_man/api
- ./alembic.ini:/usr/src/service_man/alembic.ini
# - ./alembic.ini:/usr/src/service_man/alembic.ini
command: /bin/bash -c 'cd /usr/src/service_man && poetry run uvicorn api.app:create_app --host 0.0.0.0 --reload --factory'
command: /bin/bash -c 'cd /usr/src/service_man && poetry run uvicorn api.app_builder.main:app_factory --host 0.0.0.0 --reload --factory'
# bot:
# container_name: bot

View File

@ -2,7 +2,7 @@ if __name__ == "__main__":
import uvicorn
uvicorn.run(
app="api.app:create_app",
app="api.app_builder.main:app_factory",
host="0.0.0.0",
port=8000,
reload=True,

View File

@ -1,7 +0,0 @@
from fastapi import FastAPI
def create_app() -> FastAPI:
app = FastAPI()
return app