from contextlib import asynccontextmanager from fastapi import FastAPI from core.api.kafka_publisher import KafkaPublisher from core.api.observability.metrics import ApiMetrics from core.api.observability.routes import create_observability_router from core.api.observability.stats import ( ApiRuntimeStats, HttpWorkerStatsClient, WorkerStatsProvider, ) from core.api.publisher import Publisher from core.api.routes import router from core.config import get_settings def create_app( publisher: Publisher | None = None, worker_stats_client: WorkerStatsProvider | None = None, ) -> FastAPI: settings = get_settings() api_metrics = ApiMetrics() api_stats = ApiRuntimeStats() active_worker_stats_client = ( worker_stats_client if worker_stats_client is not None else HttpWorkerStatsClient( url=settings.worker_stats_url, timeout_seconds=settings.worker_stats_timeout_seconds, ) ) @asynccontextmanager async def lifespan(app: FastAPI): active_publisher = publisher if active_publisher is None: active_publisher = KafkaPublisher( settings ) app.state.publisher = active_publisher app.state.worker_stats_client = ( active_worker_stats_client ) await active_publisher.start() try: yield finally: await active_publisher.stop() await active_worker_stats_client.close() app = FastAPI( title="Telecom CDR Ingestion API", version="0.1.0", lifespan=lifespan, ) app.state.api_metrics = api_metrics app.state.api_stats = api_stats observability_router = create_observability_router( metrics=api_metrics, stats=api_stats, worker_stats_provider=active_worker_stats_client, ) app.include_router( router, prefix="/api/v1", ) app.include_router( observability_router, ) @app.get("/health") async def health() -> dict[str, str]: return { "status": "ok", } return app app = create_app()