Skip to content

Commit

Permalink
Merge pull request #32 from ijaric/fix/#6_fix_template
Browse files Browse the repository at this point in the history
Исправление мелких багов в "Шаблоне" #6
  • Loading branch information
ijaric authored Oct 3, 2023
2 parents f74883b + 9888f0e commit a6700b4
Show file tree
Hide file tree
Showing 78 changed files with 220 additions and 2,622 deletions.
5 changes: 3 additions & 2 deletions src/fastapi_app/.env.example
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
POSTGRES_PROTOCOL=postgresql+asyncpg
POSTGRES_DRIVER=postgresql+asyncpg
POSTGRES_HOST=db
POSTGRES_PORT=5432
POSTGRES_USER=user
POSTGRES_PASSWORD=Qwe123
POSTGRES_NAME=api_db
POSTGRES_DB_NAME=api_db

NGINX_PORT=80
API_HOST=0.0.0.0
API_PORT=8000

JWT_SECRET_KEY=v9LctjUWwol4XbvczPiLFMDtZ8aal7mm
JWT_ALGORITHM=HS256

APP_RELOAD=True
2 changes: 1 addition & 1 deletion src/fastapi_app/alembic/README
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Generic single-database configuration with an async dbapi.
Generic single-database configuration with an async dbapi.
2 changes: 1 addition & 1 deletion src/fastapi_app/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
if config.config_file_name is not None:
fileConfig(config.config_file_name)

config.set_main_option("sqlalchemy.url", app_settings.settings.postgres.dsn)
config.set_main_option("sqlalchemy.url", app_settings.Settings().postgres.dsn)

target_metadata = models.Base.metadata

Expand Down
2 changes: 1 addition & 1 deletion src/fastapi_app/lib/api/v1/handlers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .health import *

__all__ = [
"health_router",
"basic_router",
]
4 changes: 2 additions & 2 deletions src/fastapi_app/lib/api/v1/handlers/health/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .liveness_probe import router as health_router
from .liveness_probe import basic_router

__all__ = [
"health_router",
"basic_router",
]
4 changes: 2 additions & 2 deletions src/fastapi_app/lib/api/v1/handlers/health/liveness_probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import lib.api.v1.schemas as api_shemas

router = fastapi.APIRouter()
basic_router = fastapi.APIRouter()


@router.get(
@basic_router.get(
"/",
response_model=api_shemas.HealthResponseModel,
summary="Статус работоспособности",
Expand Down
Empty file.
25 changes: 0 additions & 25 deletions src/fastapi_app/lib/api/v1/models/mixins.py

This file was deleted.

3 changes: 1 addition & 2 deletions src/fastapi_app/lib/api/v1/schemas/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from .base import *
from .base import HealthResponseModel

__all__ = [
"HealthResponseModel",
"TokenResponseModel",
]
7 changes: 0 additions & 7 deletions src/fastapi_app/lib/api/v1/schemas/base.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import uuid

import pydantic


class TokenResponseModel(pydantic.BaseModel):
sub: uuid.UUID
exp: int | None = None


class HealthResponseModel(pydantic.BaseModel):
status: str = pydantic.Field(default=..., examples=["healthy"], description="Схема доступности сервиса")
Empty file.
26 changes: 0 additions & 26 deletions src/fastapi_app/lib/api/v1/services/token.py

This file was deleted.

2 changes: 1 addition & 1 deletion src/fastapi_app/lib/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

__all__ = [
"Application",
"Settings",
"ApplicationError",
"DisposeError",
"Settings",
"StartServerError",
]
16 changes: 13 additions & 3 deletions src/fastapi_app/lib/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import lib.app.errors as app_errors
import lib.app.settings as app_settings
import lib.app.split_settings as app_split_settings
import lib.clients as clients

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -43,6 +44,15 @@ def from_settings(cls, settings: app_settings.Settings) -> typing.Self:
# Global clients

logger.info("Initializing global clients")
postgres_client = clients.AsyncPostgresClient(settings=settings)
http_client = clients.get_async_http_session()

disposable_resources.append(
DisposableResource(
name="postgres_client",
dispose_callback=postgres_client.dispose_callback(),
)
)

# Clients

Expand All @@ -63,10 +73,10 @@ def from_settings(cls, settings: app_settings.Settings) -> typing.Self:
# Handlers

logger.info("Initializing handlers")
# liveness_probe_handler = health_handlers.LivenessProbeHandler()
liveness_probe_handler = api_v1_handlers.basic_router


logger.info("Creating application")
# aio_app = aiohttp_web.Application()

fastapi_app = fastapi.FastAPI(
title=settings.app.title,
Expand All @@ -77,7 +87,7 @@ def from_settings(cls, settings: app_settings.Settings) -> typing.Self:
)

# Routes
fastapi_app.include_router(api_v1_handlers.health_router, prefix="/api/v1/health", tags=["health"])
fastapi_app.include_router(liveness_probe_handler, prefix="/api/v1/health", tags=["health"])

application = Application(
settings=settings,
Expand Down
57 changes: 23 additions & 34 deletions src/fastapi_app/lib/app/split_settings/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,7 @@
import lib.app.split_settings.utils as app_split_settings_utils


class DBSettings(pydantic_settings.BaseSettings):
"""Parent DB Settings Class."""

# Connection settings
protocol: str
name: str
host: str
port: int
user: str
password: pydantic.SecretStr

# Enginge settings
pool_size: int = 10
pool_pre_ping: bool = True
echo: bool = False

# Session settings
auto_commit: bool = False
auto_flush: bool = False
expire_on_commit: bool = False

@property
def dsn(self) -> str:
password = self.password.get_secret_value() if isinstance(self.password, pydantic.SecretStr) else self.password
return f"{self.protocol}://{self.user}:{password}@{self.host}:{self.port}"

@property
def dsn_as_safe_url(self) -> str:
return f"{self.protocol}://{self.user}:***@{self.host}:{self.port}"


class PostgresSettings(DBSettings):
class PostgresSettings(pydantic_settings.BaseSettings):
"""Postgres settings."""

model_config = pydantic_settings.SettingsConfigDict(
Expand All @@ -45,11 +14,31 @@ class PostgresSettings(DBSettings):
extra="ignore",
)

protocol: str = "postgresql+asyncpg"
name: str = "database_name"
# Connection settings
driver: str = "postgresql+asyncpg"
db_name: str = "database_name"
host: str = "localhost"
port: int = 5432
user: str = "app"
password: pydantic.SecretStr = pydantic.Field(
default=..., validation_alias=pydantic.AliasChoices("password", "postgres_password")
)

# Engine settings
pool_size: int = 50
pool_pre_ping: bool = True
echo: bool = False

# Session settings
auto_commit: bool = False
auto_flush: bool = False
expire_on_commit: bool = False

@property
def dsn(self) -> str:
password = self.password.get_secret_value()
return f"{self.driver}://{self.user}:{password}@{self.host}:{self.port}"

@property
def dsn_as_safe_url(self) -> str:
return f"{self.driver}://{self.user}:***@{self.host}:{self.port}"
5 changes: 4 additions & 1 deletion src/fastapi_app/lib/app/split_settings/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@


class ProjectSettings(pydantic_settings.BaseSettings):
"""Project settings."""

model_config = pydantic_settings.SettingsConfigDict(
env_file=app_split_settings_utils.ENV_PATH,
env_file_encoding="utf-8",
extra="ignore",
)

debug: str = "false"
jwt_secret_key: pydantic.SecretStr = pydantic.Field(default=..., validation_alias="jwt_secret_key")
jwt_secret_key: str = pydantic.Field(default=..., validation_alias="jwt_secret_key")
jwt_algorithm: str = "HS256"

@pydantic.field_validator("debug")
def validate_debug(cls, v: str) -> bool:
Expand Down
5 changes: 3 additions & 2 deletions src/fastapi_app/lib/clients/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .postgres import get_async_session
from .httpx import get_async_http_session
from .postgres import AsyncPostgresClient

__all__ = ["get_async_session"]
__all__ = ["AsyncPostgresClient", "get_async_http_session"]
16 changes: 16 additions & 0 deletions src/fastapi_app/lib/clients/httpx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import contextlib
import typing

import httpx


@contextlib.asynccontextmanager
async def get_async_http_session(
settings: dict[str, typing.Any] | None = None
) -> typing.AsyncGenerator[httpx.AsyncClient, None]:
"""Async http client."""
if settings is None:
settings = {}
client = httpx.AsyncClient(**settings) # Insert your own settings here
async with client as ac:
yield ac
42 changes: 24 additions & 18 deletions src/fastapi_app/lib/clients/postgres.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import sqlalchemy.ext.asyncio as sa_asyncio

import lib.app.split_settings as app_split_settings
import lib.app.settings as app_settings


async def get_async_session(
settings: app_split_settings.DBSettings,
) -> sa_asyncio.async_sessionmaker[sa_asyncio.AsyncSession]:
engine = sa_asyncio.create_async_engine(
url=settings.dsn,
pool_size=settings.pool_size,
pool_pre_ping=settings.pool_pre_ping,
echo=settings.echo,
future=True,
)
class AsyncPostgresClient:
"""Async Postgres Client that return sessionmaker."""

async_session = sa_asyncio.async_sessionmaker(
bind=engine,
autocommit=settings.auto_commit,
autoflush=settings.auto_flush,
expire_on_commit=settings.expire_on_commit,
)
def __init__(self, settings: app_settings.Settings) -> None:
self.settings = settings.postgres
self.async_enging = sa_asyncio.create_async_engine(
url=self.settings.dsn,
pool_size=self.settings.pool_size,
pool_pre_ping=self.settings.pool_pre_ping,
echo=self.settings.echo,
future=True,
)

return async_session # noqa: RET504
def get_async_session(self) -> sa_asyncio.async_sessionmaker[sa_asyncio.AsyncSession]:
async_session = sa_asyncio.async_sessionmaker(
bind=self.async_enging,
autocommit=self.settings.auto_commit,
autoflush=self.settings.auto_flush,
expire_on_commit=self.settings.expire_on_commit,
)

return async_session # noqa: RET504

async def dispose_callback(self) -> None:
await self.async_enging.dispose()
5 changes: 3 additions & 2 deletions src/fastapi_app/lib/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .base_sqlalchemy import Base
from .base_sqlalchemy import Base, IdCreatedUpdatedBaseMixin
from .token import Token

__all__ = ["Base"]
__all__ = ["Base", "IdCreatedUpdatedBaseMixin", "Token"]
31 changes: 31 additions & 0 deletions src/fastapi_app/lib/models/base_sqlalchemy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
import uuid

import sqlalchemy
import sqlalchemy.dialects.postgresql
import sqlalchemy.ext.declarative
import sqlalchemy.orm


class Base(sqlalchemy.orm.DeclarativeBase):
"""Base class for all models."""

pass


class IdCreatedUpdatedBaseMixin(Base):
@sqlalchemy.ext.declarative.declared_attr
def uuid(cls):
return sqlalchemy.Column(
sqlalchemy.dialects.postgresql.UUID(as_uuid=True),
primary_key=True,
default=uuid.uuid4,
unique=True,
nullable=False,
)

@sqlalchemy.ext.declarative.declared_attr
def created_at(cls):
return sqlalchemy.Column(sqlalchemy.DateTime, server_default=sqlalchemy.sql.func.now())

@sqlalchemy.ext.declarative.declared_attr
def updated_at(cls):
return sqlalchemy.Column(sqlalchemy.DateTime, server_default=sqlalchemy.sql.func.now())

@sqlalchemy.ext.declarative.declared_attr.directive
def __tablename__(cls) -> str:
return cls.__name__.lower()
Loading

0 comments on commit a6700b4

Please sign in to comment.