Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/sqlite repo #56

Merged
merged 14 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
336 changes: 319 additions & 17 deletions poetry.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ Hypercorn = "^0.17.3"
MarkupSafe = "^2.1.3"
uvloop = {version = "^0.20.0", markers = "sys_platform != 'win32'"}
pydantic-settings = {version = "^2.3.4", markers = "sys_platform != 'win32'"}
cython = "^3.0.11"
sqlalchemy = {version="^2.0.23", markers = "sys_platform != 'win32'", extras = ["asyncio"]}
alembic = "^1.13.2"
aiosqlite = "^0.20.0"
sqlmodel = {version="^0.0.21"}

# [tool.poetry.group.dev.dependencies]

Expand Down
90 changes: 90 additions & 0 deletions xcov19/app/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from collections.abc import AsyncGenerator
import sys
from rodi import Container
from sqlmodel import SQLModel

from xcov19.app.settings import Settings
from sqlalchemy.ext.asyncio import (
create_async_engine,
AsyncEngine,
AsyncSession,
async_sessionmaker,
)

import logging
from sqlalchemy.pool import AsyncAdaptedQueuePool

db_logger = logging.getLogger(__name__)
db_fmt = logging.Formatter(
"DATABASE:%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setFormatter(db_fmt)

db_logger.setLevel(logging.INFO)
db_logger.addHandler(stream_handler)


class SessionFactory:
"""Class to remember sessionmaker factory constructor for DI container.

Use like this to retrieve sessionmaker from DI container:
container.resolve(SessionFactory)

It is already added as in `configure_database_session`:
container.add_singleton_by_factory(SessionFactory(engine), SessionFactory)
"""

def __init__(self, engine: AsyncEngine):
self._engine = engine

def __call__(self) -> async_sessionmaker[AsyncSession]:
return async_sessionmaker(
self._engine, class_=AsyncSession, expire_on_commit=False
)


async def setup_database(engine: AsyncEngine) -> None:
"""Sets up tables for database."""
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)


async def create_async_session(
AsyncSessionFactory: async_sessionmaker[AsyncSession],
) -> AsyncGenerator[AsyncSession, None]:
"""Create an asynchronous database session."""
async with AsyncSessionFactory() as session:
try:
yield session
finally:
await session.close()


async def start_db_session(container: Container):
"""Starts a new database session given SessionFactory."""
# add LocalAsyncSession
local_async_session = create_async_session(
container.resolve(async_sessionmaker[AsyncSession])
)
container.add_instance(local_async_session, AsyncSession)


def configure_database_session(container: Container, settings: Settings) -> Container:
"""Configure database session setup for the application."""
# add engine
db_logger.info(f"""====== Configuring database session. ======
DB_ENGINE_URL: {settings.db_engine_url}
""")
engine = create_async_engine(
settings.db_engine_url, echo=True, poolclass=AsyncAdaptedQueuePool
)
container.add_instance(engine, AsyncEngine)

# add sessionmaker
container.add_singleton_by_factory(
SessionFactory(engine), async_sessionmaker[AsyncSession]
)

db_logger.info("====== Database session configured. ======")
return container
20 changes: 19 additions & 1 deletion xcov19/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
"""

from blacksheep import Application
from rodi import Container
from rodi import Container, ContainerProtocol

from xcov19.app.database import (
configure_database_session,
setup_database,
start_db_session,
)
from xcov19.app.auth import configure_authentication
from xcov19.app.controllers import controller_router
from xcov19.app.docs import configure_docs
Expand All @@ -13,6 +18,8 @@
from xcov19.app.services import configure_services
from xcov19.app.settings import load_settings, Settings

from sqlalchemy.ext.asyncio import AsyncEngine


def configure_application(
services: Container,
Expand All @@ -27,7 +34,18 @@ def configure_application(
configure_authentication(app, settings)
configure_middleware(app, origin_header_middleware)
configure_docs(app, settings)
configure_database_session(services, settings)
return app


app = configure_application(*configure_services(load_settings()))


@app.on_start
async def on_start():
container: ContainerProtocol = app.services
if not isinstance(container, Container):
raise ValueError("Container is not a valid container")
await start_db_session(container)
engine = container.resolve(AsyncEngine)
await setup_database(engine)
1 change: 1 addition & 0 deletions xcov19/app/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from rodi import Container


from xcov19.app.settings import Settings
from xcov19.services.geolocation import (
LocationQueryServiceInterface,
Expand Down
2 changes: 2 additions & 0 deletions xcov19/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class Settings(BaseSettings):
# export app_app='{"show_error_details": True}'
app: App = App()

db_engine_url: str = "sqlite+aiosqlite:///" # "sqlite+aiosqlite:///xcov19.db"

model_config = SettingsConfigDict(env_prefix="APP_")


Expand Down
3 changes: 3 additions & 0 deletions xcov19/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@

config.bind = [f"0.0.0.0:{port}"]
config.debug = True
config.accesslog = "-"
config.errorlog = "-"

config.use_reloader = True

asyncio.run(serve(app, config))
Loading