feat(api): add POST/GET /v1/files with auth, error envelope, and request-id middleware
Why: - Wires the ADR-0008 error envelope, per-request correlation id, and the /v1/files routes into the app. Changes: - Extend AppResources/lifespan with the ingestion CapacityLimiter and ObjectStorage adapter. Impact: - /v1 now exposes routes for the first time.
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
from collections.abc import AsyncIterator
|
||||
from dataclasses import dataclass
|
||||
|
||||
from anyio import CapacityLimiter
|
||||
from fastapi import Request
|
||||
from minio import Minio
|
||||
from qdrant_client import AsyncQdrantClient
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
|
||||
|
||||
from src.application.ports.object_storage import ObjectStorage
|
||||
from src.config import Settings
|
||||
|
||||
|
||||
@@ -16,6 +18,8 @@ class AppResources:
|
||||
db_sessionmaker: async_sessionmaker[AsyncSession]
|
||||
minio_client: Minio
|
||||
qdrant_client: AsyncQdrantClient
|
||||
object_storage: ObjectStorage
|
||||
ingestion_limiter: CapacityLimiter
|
||||
|
||||
|
||||
def _resources(request: Request) -> AppResources:
|
||||
@@ -34,6 +38,25 @@ def get_qdrant_client(request: Request) -> AsyncQdrantClient:
|
||||
return _resources(request).qdrant_client
|
||||
|
||||
|
||||
def get_object_storage(request: Request) -> ObjectStorage:
|
||||
return _resources(request).object_storage
|
||||
|
||||
|
||||
def get_ingestion_limiter(request: Request) -> CapacityLimiter:
|
||||
return _resources(request).ingestion_limiter
|
||||
|
||||
|
||||
def get_sessionmaker(request: Request) -> async_sessionmaker[AsyncSession]:
|
||||
"""The session *factory*, not a request-scoped session.
|
||||
|
||||
Application services that own more than one transaction in a single
|
||||
request (ADR-0017's two-phase upload) need to open and close sessions
|
||||
themselves rather than borrow one request-scoped session that would
|
||||
otherwise stay open across the whole request.
|
||||
"""
|
||||
return _resources(request).db_sessionmaker
|
||||
|
||||
|
||||
async def get_db_session(request: Request) -> AsyncIterator[AsyncSession]:
|
||||
sessionmaker = _resources(request).db_sessionmaker
|
||||
async with sessionmaker() as session:
|
||||
|
||||
@@ -2,13 +2,14 @@ from collections.abc import AsyncIterator, Callable
|
||||
from contextlib import AbstractAsyncContextManager, asynccontextmanager
|
||||
|
||||
import structlog
|
||||
from anyio import to_thread
|
||||
from anyio import CapacityLimiter, to_thread
|
||||
from fastapi import FastAPI
|
||||
|
||||
from src.application.ingestion import get_encoder
|
||||
from src.bootstrap.dependencies import AppResources
|
||||
from src.config import Settings
|
||||
from src.infrastructure.minio.client import create_client as create_minio_client
|
||||
from src.infrastructure.minio.storage import MinioObjectStorage
|
||||
from src.infrastructure.observability.logging import configure_logging
|
||||
from src.infrastructure.postgres.database import create_engine, create_sessionmaker
|
||||
from src.infrastructure.qdrant.client import create_client as create_qdrant_client
|
||||
@@ -43,12 +44,22 @@ def create_lifespan(
|
||||
qdrant_client = create_qdrant_client(resolved_settings.qdrant)
|
||||
logger.info("lifespan.qdrant.client.created")
|
||||
|
||||
# Bounds threads spent on blocking ingestion work (parsing, chunking,
|
||||
# hashing, the sync minio SDK) so it cannot exhaust Starlette's own
|
||||
# thread pool (ADR-0017).
|
||||
ingestion_limiter = CapacityLimiter(resolved_settings.ingestion.thread_pool_size)
|
||||
object_storage = MinioObjectStorage(
|
||||
minio_client, bucket=resolved_settings.minio.bucket, limiter=ingestion_limiter
|
||||
)
|
||||
|
||||
app.state.resources = AppResources(
|
||||
settings=resolved_settings,
|
||||
db_engine=db_engine,
|
||||
db_sessionmaker=db_sessionmaker,
|
||||
minio_client=minio_client,
|
||||
qdrant_client=qdrant_client,
|
||||
object_storage=object_storage,
|
||||
ingestion_limiter=ingestion_limiter,
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user