"""AnalysisDataSourceStore — read per-analysis data-source bindings (#10). The dedorch `data_sources` table records which catalog sources an analysis is scoped to (`reference_id` = the catalog source id). It's written at `/analysis/create`; this store is the read seam for the two consumers — `structured_flow` catalog scoping and the report's data-source appendix. Fail-open by convention at the call sites: an empty binding (legacy room, or the FE not yet sending ids) means "no restriction" — fall back to the whole catalog. Mirrors `AnalysisStateStore`: each call opens its own `AsyncSession`. """ from __future__ import annotations from sqlalchemy import select from src.db.postgres.connection import AsyncSessionLocal from src.db.postgres.models import AnalysisDataSourceRow from src.middlewares.logging import get_logger logger = get_logger("binding_store") class AnalysisDataSourceStore: """Read the bound catalog `source_id`s for an analysis.""" async def get(self, analysis_id: str) -> list[str]: async with AsyncSessionLocal() as session: result = await session.execute( select(AnalysisDataSourceRow.reference_id).where( AnalysisDataSourceRow.analysis_id == analysis_id ) ) return list(result.scalars().all())