feat: implement governed reporting vertical

This commit is contained in:
2026-08-01 17:48:39 +02:00
parent 720959536b
commit eba35edd3c
35 changed files with 8414 additions and 33 deletions
+135
View File
@@ -0,0 +1,135 @@
from __future__ import annotations
from collections.abc import Mapping
from dataclasses import dataclass, field
from datetime import datetime
from typing import Protocol, runtime_checkable
CAPABILITY_REPORTING_REGISTRY = "reporting.registry"
CAPABILITY_REPORTING_RUNNER = "reporting.runner"
CAPABILITY_REPORTING_SCHEDULER = "reporting.scheduler"
CAPABILITY_REPORTING_CHART_RENDERER = "reporting.chart_renderer"
@dataclass(frozen=True, slots=True)
class ReportingDatasetReadRequest:
source_ref: str
source_revision: int | None
parameters: Mapping[str, object] = field(default_factory=dict)
row_limit: int = 2_000
expected_definition_hash: str | None = None
expected_source_fingerprints: tuple[Mapping[str, object], ...] = ()
@dataclass(frozen=True, slots=True)
class ReportingDatasetReadResult:
rows: tuple[Mapping[str, object], ...]
total_rows: int
truncated: bool
output_hash: str
executor_version: str
definition_hash: str | None = None
source_fingerprints: tuple[Mapping[str, object], ...] = ()
diagnostics: tuple[Mapping[str, object], ...] = ()
generated_at: datetime | None = None
provenance: Mapping[str, object] = field(default_factory=dict)
@runtime_checkable
class ReportingReadModelProvider(Protocol):
def read_dataset(
self,
session: object,
principal: object,
*,
request: ReportingDatasetReadRequest,
) -> ReportingDatasetReadResult: ...
@dataclass(frozen=True, slots=True)
class ReportingRowPolicyRequest:
dataset_id: str
dataset_revision: int
policy_ref: str
rows: tuple[Mapping[str, object], ...]
@dataclass(frozen=True, slots=True)
class ReportingRowPolicyResult:
rows: tuple[Mapping[str, object], ...]
decision_ref: str
provenance: Mapping[str, object] = field(default_factory=dict)
@runtime_checkable
class ReportingRowPolicyProvider(Protocol):
def authorize_rows(
self,
session: object,
principal: object,
*,
request: ReportingRowPolicyRequest,
) -> ReportingRowPolicyResult: ...
@dataclass(frozen=True, slots=True)
class ReportingPublicationPayload:
publication_id: str
execution_id: str
tenant_id: str
report_id: str
report_revision: int
format: str
target_ref: str | None
rows: tuple[Mapping[str, object], ...]
schema: tuple[Mapping[str, object], ...]
output_hash: str
options: Mapping[str, object] = field(default_factory=dict)
@runtime_checkable
class ReportingPublicationTarget(Protocol):
def publish_report(
self,
session: object,
principal: object,
*,
payload: ReportingPublicationPayload,
) -> Mapping[str, object]: ...
@runtime_checkable
class ReportingChartRenderer(Protocol):
def render(
self, *, visualization: object, result: object
) -> Mapping[str, object]: ...
def capability(registry: object | None, name: str) -> object | None:
if (
registry is None
or not hasattr(registry, "has_capability")
or not hasattr(registry, "capability")
or not registry.has_capability(name)
):
return None
return registry.capability(name)
__all__ = [
"CAPABILITY_REPORTING_CHART_RENDERER",
"CAPABILITY_REPORTING_REGISTRY",
"CAPABILITY_REPORTING_RUNNER",
"CAPABILITY_REPORTING_SCHEDULER",
"ReportingChartRenderer",
"ReportingDatasetReadRequest",
"ReportingDatasetReadResult",
"ReportingPublicationPayload",
"ReportingPublicationTarget",
"ReportingReadModelProvider",
"ReportingRowPolicyProvider",
"ReportingRowPolicyRequest",
"ReportingRowPolicyResult",
"capability",
]