StartFromBegining
parent
08e6aa5cb1
commit
b733a6bf9a
46
api/app.py
46
api/app.py
|
@ -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()
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .main import app_factory
|
||||||
|
|
||||||
|
__all__ = ("app_factory",)
|
|
@ -0,0 +1,9 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
from .routers import init_routers
|
||||||
|
|
||||||
|
|
||||||
|
def app_factory() -> FastAPI:
|
||||||
|
app = FastAPI()
|
||||||
|
init_routers(app)
|
||||||
|
return app
|
|
@ -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)
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .ping_response import PingResponse
|
||||||
|
|
||||||
|
__all__ = ("PingResponse",)
|
|
@ -2,6 +2,5 @@ from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class TokenDTO:
|
class PingResponse:
|
||||||
access_token: str
|
status: str
|
||||||
token_type: str
|
|
|
@ -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"]),
|
|
||||||
)
|
|
|
@ -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)
|
|
|
@ -1,9 +0,0 @@
|
||||||
from .base_model import BaseModel
|
|
||||||
from .refers_model import RefersModel
|
|
||||||
from .user_model import UserModel
|
|
||||||
|
|
||||||
__all__ = (
|
|
||||||
"BaseModel",
|
|
||||||
"UserModel",
|
|
||||||
"RefersModel",
|
|
||||||
)
|
|
|
@ -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,
|
|
||||||
)
|
|
|
@ -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]
|
|
|
@ -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]
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .ping import healthcheck_router
|
||||||
|
|
||||||
|
__all__ = ("healthcheck_router",)
|
|
@ -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")
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -1,3 +0,0 @@
|
||||||
from .user import UserService
|
|
||||||
|
|
||||||
__all__ = ("UserService",)
|
|
|
@ -42,9 +42,9 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- ./config/api_config.yml:/usr/src/service_man/config/api_config.yml
|
- ./config/api_config.yml:/usr/src/service_man/config/api_config.yml
|
||||||
- ./api:/usr/src/service_man/api
|
- ./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:
|
# bot:
|
||||||
# container_name: bot
|
# container_name: bot
|
||||||
|
|
|
@ -2,7 +2,7 @@ if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
app="api.app:create_app",
|
app="api.app_builder.main:app_factory",
|
||||||
host="0.0.0.0",
|
host="0.0.0.0",
|
||||||
port=8000,
|
port=8000,
|
||||||
reload=True,
|
reload=True,
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
|
|
||||||
def create_app() -> FastAPI:
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
return app
|
|
Loading…
Reference in New Issue