Initial commit
Some checks failed
CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
CI/CD Pipeline / Policy Validation (push) Has been cancelled
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-firm-connectors) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-forms) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-hmrc) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ingestion) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-normalize-map) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ocr) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-indexer) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-reason) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rpa) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (ui-review) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (ui-review) (push) Has been cancelled
CI/CD Pipeline / Generate SBOM (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Notifications (push) Has been cancelled
Some checks failed
CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
CI/CD Pipeline / Policy Validation (push) Has been cancelled
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-firm-connectors) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-forms) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-hmrc) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ingestion) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-normalize-map) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ocr) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-indexer) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-reason) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rpa) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (ui-review) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (ui-review) (push) Has been cancelled
CI/CD Pipeline / Generate SBOM (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Notifications (push) Has been cancelled
This commit is contained in:
123
libs/app_factory.py
Normal file
123
libs/app_factory.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""Factory for creating FastAPI applications with consistent setup."""
|
||||
|
||||
# FILE: libs/app_factory.py
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from libs.config import BaseAppSettings, get_default_settings
|
||||
from libs.observability import setup_observability
|
||||
from libs.schemas import ErrorResponse
|
||||
from libs.security import get_current_user, get_tenant_id
|
||||
from libs.security.middleware import TrustedProxyMiddleware
|
||||
|
||||
|
||||
def create_trusted_proxy_middleware(
|
||||
internal_cidrs: list[str], disable_auth: bool = False
|
||||
) -> TrustedProxyMiddleware:
|
||||
"""Create a TrustedProxyMiddleware instance with the given internal CIDRs."""
|
||||
|
||||
# This is a factory function that will be called by FastAPI's add_middleware
|
||||
# We return a partial function that creates the middleware
|
||||
def middleware_factory(app: Any) -> TrustedProxyMiddleware:
|
||||
return TrustedProxyMiddleware(app, internal_cidrs, disable_auth)
|
||||
|
||||
return middleware_factory # type: ignore
|
||||
|
||||
|
||||
def create_app( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
service_name: str,
|
||||
title: str,
|
||||
description: str,
|
||||
version: str = "1.0.0",
|
||||
settings_class: type[BaseAppSettings] = BaseAppSettings,
|
||||
custom_settings: dict[str, Any] | None = None,
|
||||
) -> tuple[FastAPI, BaseAppSettings]:
|
||||
"""Create a FastAPI application with standard configuration"""
|
||||
|
||||
# Create settings
|
||||
settings_kwargs = {"service_name": service_name}
|
||||
if custom_settings:
|
||||
settings_kwargs.update(custom_settings)
|
||||
|
||||
settings = get_default_settings(**settings_kwargs)
|
||||
if settings_class != BaseAppSettings:
|
||||
# Use custom settings class
|
||||
settings = settings_class(**settings_kwargs) # type: ignore
|
||||
|
||||
# Create lifespan context manager
|
||||
@asynccontextmanager
|
||||
async def lifespan(
|
||||
app: FastAPI,
|
||||
) -> AsyncIterator[None]: # pylint: disable=unused-argument
|
||||
# Startup
|
||||
setup_observability(settings)
|
||||
yield
|
||||
# Shutdown
|
||||
|
||||
# Create FastAPI app
|
||||
app = FastAPI(
|
||||
title=title, description=description, version=version, lifespan=lifespan
|
||||
)
|
||||
|
||||
# Add middleware
|
||||
app.add_middleware(
|
||||
TrustedProxyMiddleware,
|
||||
internal_cidrs=settings.internal_cidrs,
|
||||
disable_auth=getattr(settings, "disable_auth", False),
|
||||
)
|
||||
|
||||
# Add exception handlers
|
||||
@app.exception_handler(HTTPException)
|
||||
async def http_exception_handler(
|
||||
request: Request, exc: HTTPException
|
||||
) -> JSONResponse:
|
||||
"""Handle HTTP exceptions with RFC7807 format"""
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=ErrorResponse(
|
||||
type=f"https://httpstatuses.com/{exc.status_code}",
|
||||
title=exc.detail,
|
||||
status=exc.status_code,
|
||||
detail=exc.detail,
|
||||
instance=str(request.url),
|
||||
trace_id=getattr(request.state, "trace_id", None),
|
||||
).model_dump(),
|
||||
)
|
||||
|
||||
# Add health endpoints
|
||||
@app.get("/healthz")
|
||||
async def health_check() -> dict[str, str]:
|
||||
"""Health check endpoint"""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": settings.service_name,
|
||||
"version": version,
|
||||
}
|
||||
|
||||
@app.get("/readyz")
|
||||
async def readiness_check() -> dict[str, str]:
|
||||
"""Readiness check endpoint"""
|
||||
return {"status": "ready", "service": settings.service_name, "version": version}
|
||||
|
||||
@app.get("/livez")
|
||||
async def liveness_check() -> dict[str, str]:
|
||||
"""Liveness check endpoint"""
|
||||
return {"status": "alive", "service": settings.service_name, "version": version}
|
||||
|
||||
return app, settings
|
||||
|
||||
|
||||
# Dependency factories
|
||||
def get_user_dependency() -> Any:
|
||||
"""Get user dependency function"""
|
||||
return get_current_user()
|
||||
|
||||
|
||||
def get_tenant_dependency() -> Any:
|
||||
"""Get tenant dependency function"""
|
||||
return get_tenant_id()
|
||||
Reference in New Issue
Block a user