diff options
Diffstat (limited to 'src/srht_contrib/main.py')
| -rw-r--r-- | src/srht_contrib/main.py | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/src/srht_contrib/main.py b/src/srht_contrib/main.py new file mode 100644 index 0000000..37c3492 --- /dev/null +++ b/src/srht_contrib/main.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager + +from apscheduler.schedulers.background import BackgroundScheduler +from fastapi import FastAPI +from sqlalchemy import Engine +from sqlalchemy.orm import Session, sessionmaker + +from srht_contrib.api.routes_contributions import router as contributions_router +from srht_contrib.api.routes_health import router as health_router +from srht_contrib.api.routes_repositories import router as repositories_router +from srht_contrib.config import Settings, get_settings +from srht_contrib.db import make_engine, validate_db +from srht_contrib.jobs.poller import PollerService +from srht_contrib.logging import configure_logging +from srht_contrib.services.git import GitIngestionService +from srht_contrib.services.srht_client import SourceHutGraphQLClient +from srht_contrib.services.todo import TodoIngestionService +from srht_contrib.utils.identity import ActorIdentityResolver + + +def build_poller(settings: Settings) -> PollerService: + todo_client = SourceHutGraphQLClient(settings.todo_srht_endpoint, settings.srht_token) + git_client = SourceHutGraphQLClient(settings.git_srht_endpoint, settings.srht_token) + todo_service = TodoIngestionService(todo_client, settings) + git_service = GitIngestionService(git_client, settings) + return PollerService(todo_service=todo_service, git_service=git_service) + + +def create_app( + settings: Settings | None = None, + *, + engine: Engine | None = None, + session_factory: sessionmaker[Session] | None = None, + poller: PollerService | None = None, +) -> FastAPI: + app_settings = settings or get_settings() + configure_logging() + app_engine = engine or make_engine(app_settings) + app_session_factory = session_factory or sessionmaker( + bind=app_engine, + autoflush=False, + autocommit=False, + expire_on_commit=False, + ) + actor_identity_resolver = ActorIdentityResolver(app_settings.actor_aliases_json) + + @asynccontextmanager + async def lifespan(app: FastAPI) -> AsyncIterator[None]: + validate_db(app_engine) + app_poller = poller or build_poller(app_settings) + app.state.poller = app_poller + app.state.settings = app_settings + app.state.engine = app_engine + app.state.session_factory = app_session_factory + app.state.actor_identity_resolver = actor_identity_resolver + scheduler: BackgroundScheduler | None = None + if app_settings.enable_scheduler: + scheduler = BackgroundScheduler() + scheduler.add_job( + _scheduled_poll, + "interval", + seconds=app_settings.poll_interval_seconds, + args=[app], + id="srht-poller", + replace_existing=True, + ) + scheduler.start() + app.state.scheduler = scheduler + try: + yield + finally: + if scheduler is not None: + scheduler.shutdown(wait=False) + app_poller.todo_service.client.close() + app_poller.git_service.client.close() + + app = FastAPI(title=app_settings.app_name, lifespan=lifespan) + + app.include_router(health_router) + app.include_router(contributions_router) + app.include_router(repositories_router) + return app + + +def _scheduled_poll(app: FastAPI) -> None: + settings: Settings = app.state.settings + poller: PollerService = app.state.poller + session_factory: sessionmaker[Session] = app.state.session_factory + db = session_factory() + try: + poller.poll_all(db, settings.default_actor) + finally: + db.close() + + +app = create_app() |
