Skip to content

Refactor on startup configuration, sqlalchemy session maker, and email sender factory. #103

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

Merged
merged 3 commits into from
Apr 17, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ venv.bak/

# Rope project settings
.ropeproject
.vscode

# mkdocs documentation
/site
Expand Down
36 changes: 17 additions & 19 deletions src/labs/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
API endpoints are built and served using the FastAPI micro-framework.

"""
from contextlib import asynccontextmanager
from . import __title__, __version__

from fastapi import FastAPI, Request, status, WebSocket
from fastapi.responses import JSONResponse
from fastapi.routing import APIRoute

from .settings import settings
Expand All @@ -39,19 +39,33 @@ def generate_operation_id(route: APIRoute) -> str:
return route.name


@asynccontextmanager
async def lifespan(_fastapi: FastAPI):
if broker.is_worker_process:
# TaskIQ configurartion so we can share FastAPI dependencies in tasks
await broker.startup()

yield

if broker.is_worker_process:
# On shutdown, we need to shutdown the broker
await broker.shutdown()


"""A FastAPI application that serves handlers
"""
app = FastAPI(
title=__title__,
version=__version__,
description=settings.api_router.__doc__,
description=str(settings.api_router.__doc__),
docs_url=settings.api_router.path_docs,
root_path=settings.api_router.path_root,
terms_of_service=settings.api_router.terms_of_service,
contact=settings.api_router.contact,
license_info=settings.api_router.license_info,
openapi_tags=settings.api_router.open_api_tags,
generate_unique_id_function=generate_operation_id,
lifespan=lifespan
)


Expand All @@ -67,23 +81,7 @@ async def websocket_endpoint(websocket: WebSocket):
app.include_router(router_root)


# TaskIQ configurartion so we can share FastAPI dependencies in tasks
@app.on_event("startup")
async def app_startup():
if not broker.is_worker_process:
await broker.startup()

# On shutdown, we need to shutdown the broker


@app.on_event("shutdown")
async def app_shutdown():
if not broker.is_worker_process:
await broker.shutdown()

# Default handler


@app.get(
"/",
status_code=status.HTTP_200_OK,
Expand All @@ -93,5 +91,5 @@ async def root(request: Request) -> RootResponse:
"""
return RootResponse(
message="Welcome to the {} API".format(__name__),
root_path=request.scope.get("root_path")
root_path=str(request.scope.get("root_path"))
)
18 changes: 8 additions & 10 deletions src/labs/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

"""

from typing import AsyncGenerator
from sqlalchemy.ext.asyncio import create_async_engine,\
AsyncSession
AsyncSession, async_sessionmaker, AsyncAttrs
from sqlalchemy.orm import DeclarativeBase,\
configure_mappers, sessionmaker
configure_mappers


from .settings import settings
Expand All @@ -24,19 +25,16 @@
configure_mappers()

# Get an async session from the engine
AsyncSessionFactory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)


async def get_async_session() -> AsyncSession:
async_session = sessionmaker(
engine, class_=AsyncSession, expire_on_commit=False
)
async with async_session() as session:
async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
async with AsyncSessionFactory() as session:
yield session

# Used by the ORM layer to describe models


class Base(DeclarativeBase):
# Used by the ORM layer to describe models
class Base(DeclarativeBase, AsyncAttrs):
"""
SQLAlchemy 2.0 style declarative base class
https://bit.ly/3WE3Srg
Expand Down
15 changes: 13 additions & 2 deletions src/labs/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,22 @@
Redmail docs are located at https://red-mail.readthedocs.io/
"""
import os
from redmail import EmailSender
from redmail.email.sender import EmailSender

from .settings import settings

sender = EmailSender(
# Custom factory to be able to set the sender globally
class EmailSenderFactory(EmailSender):
@property
def sender(self):
return self.sender

@sender.setter
def sender(self, sender: str):
self.sender = sender


sender = EmailSenderFactory(
host=settings.smtp.host,
port=settings.smtp.port,
username=settings.smtp.user.get_secret_value(),
Expand Down