Complete governed reporting execution and publication

This commit is contained in:
2026-08-04 02:23:24 +02:00
parent a81151391b
commit 8337aec19a
29 changed files with 3611 additions and 81 deletions
+263
View File
@@ -8,6 +8,10 @@ from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from govoplan_core.db.base import Base
from govoplan_core.core.files import (
CAPABILITY_FILES_ARTIFACT_STORE,
ManagedArtifactRef,
)
from govoplan_core.security.module_permissions import scopes_grant_compatible
from govoplan_reporting.backend.definitions import (
ADMIN_SCOPE,
@@ -27,6 +31,11 @@ from govoplan_reporting.backend.execution import (
execute_report,
run_quality_plan,
)
from govoplan_reporting.backend.drilldown import (
ReportingDrillError,
create_drill_context,
resolve_drill_context,
)
from govoplan_reporting.backend.operations import (
IMPORT_SCOPE,
PUBLISH_SCOPE,
@@ -35,10 +44,24 @@ from govoplan_reporting.backend.operations import (
assess_import,
dispatch_due_schedules,
export_execution,
list_publications,
publish_execution,
upsert_saved_view,
upsert_schedule,
)
from govoplan_reporting.backend.schemas import ReportQuery
from govoplan_reporting.backend.postgres_planner import compile_postgres_query
from govoplan_reporting.backend.contracts import (
CAPABILITY_REPORTING_PUBLICATION_FILES,
)
from govoplan_reporting.backend.publication_targets import (
FilesReportingPublicationTarget,
publication_target_catalog,
)
from govoplan_reporting.backend.schemas import (
DatasetDefinition,
SemanticModelDefinition,
)
NOW = datetime(2026, 8, 1, 10, 0, tzinfo=UTC)
@@ -79,6 +102,36 @@ class Principal:
return scopes_grant_compatible(self.scopes, scope)
class CapabilityRegistry:
def __init__(self) -> None:
self.providers: dict[str, object] = {}
def has_capability(self, name: str) -> bool:
return name in self.providers
def capability(self, name: str) -> object | None:
return self.providers.get(name)
class ArtifactStore:
def __init__(self) -> None:
self.requests: list[object] = []
def store_artifact(self, session, principal, *, request):
del session, principal
self.requests.append(request)
return ManagedArtifactRef(
file_asset_id="asset-1",
file_version_id="version-1",
filename=request.filename,
display_path=f"{request.folder}/{request.filename}",
content_type=request.content_type,
size_bytes=len(request.payload),
sha256="a" * 64,
provenance={"stored": True},
)
class ReportingServiceTests(unittest.TestCase):
def setUp(self) -> None:
self.engine = create_engine("sqlite+pysqlite:///:memory:")
@@ -404,6 +457,216 @@ class ReportingServiceTests(unittest.TestCase):
accepted_approximations=[],
)
def test_drill_context_is_bounded_actor_bound_and_reauthorized(self) -> None:
self._create_report_graph()
execution = execute_report(
self.session,
self.principal,
registry=None,
report_id="report-1",
report_revision=1,
parameters={},
query=None,
idempotency_key="drill-source",
)
north = next(row for row in execution["rows"] if row["region"] == "North")
context = create_drill_context(
self.session,
self.principal,
registry=None,
execution_id=str(execution["execution_id"]),
aggregate_row=north,
limit=50,
)
detail = resolve_drill_context(
self.session,
self.principal,
registry=None,
token=str(context["token"]),
)
self.assertEqual(2, detail["total_rows"])
self.assertEqual({"North"}, {row["region"] for row in detail["rows"]})
self.assertEqual("region", detail["dimension_path"][0]["dimension"])
with self.assertRaises(PermissionError):
resolve_drill_context(
self.session,
Principal(account_id="another-analyst"),
registry=None,
token=str(context["token"]),
)
with self.assertRaises(ReportingDrillError):
create_drill_context(
self.session,
self.principal,
registry=None,
execution_id=str(execution["execution_id"]),
aggregate_row={"region": "Not an execution row"},
limit=50,
)
def test_governance_scope_inheritance_never_broadens_parent_limits(self) -> None:
system = Principal(
scopes=(*ALL_SCOPES, "system:governance:write"),
group_ids=("group-reporters",),
)
dataset = dataset_payload()
dataset["governance"] = {
"scope_type": "system",
"inherit_to_lower_scopes": True,
"allow_run": True,
"allow_reuse": True,
"allow_automation": False,
}
self._create("dataset", "dataset-governed", dataset, principal=system)
semantic = semantic_payload(dataset_id="dataset-governed")
semantic["governance"] = {
"scope_type": "tenant",
"inherit_to_lower_scopes": True,
"allow_run": True,
"allow_reuse": True,
"allow_automation": True,
}
with self.assertRaisesRegex(ValueError, "cannot broaden inherited limits"):
self._create(
"semantic_model",
"semantic-broadened",
semantic,
principal=system,
)
semantic["governance"]["allow_automation"] = False
semantic_record = self._create(
"semantic_model",
"semantic-governed",
semantic,
principal=system,
)
semantic_governance = semantic_record.payload["governance"]
self.assertEqual("system", semantic_governance["source_scope"]["scope_type"])
self.assertFalse(
semantic_governance["source_effective_limits"]["allow_automation"]
)
report = report_payload()
report["semantic_model_id"] = "semantic-governed"
report["governance"] = {
"scope_type": "group",
"scope_id": "group-reporters",
"inherit_to_lower_scopes": False,
"allow_run": True,
"allow_reuse": False,
"allow_automation": False,
}
self._create("report", "report-governed", report, principal=system)
self.assertIsNotNone(
get_definition(
self.session,
system,
definition_kind="report",
definition_id="report-governed",
)
)
self.assertIsNone(
get_definition(
self.session,
Principal(account_id="outsider"),
definition_kind="report",
definition_id="report-governed",
)
)
def test_postgres_plan_is_bounded_and_parameterized(self) -> None:
dataset = DatasetDefinition.model_validate(dataset_payload())
semantic = SemanticModelDefinition.model_validate(semantic_payload())
query = ReportQuery.model_validate(
{
"mode": "summary",
"dimensions": ["region"],
"measures": ["amount", "value_per_case"],
"filters": [
{
"dimension": "region",
"operator": "contains",
"value": "North%' OR TRUE --",
}
],
"sort": [{"key": "amount", "direction": "desc"}],
"limit": 25,
}
)
plan = compile_postgres_query(dataset, semantic, query)
self.assertIn("GROUP BY", plan.sql)
self.assertIn("LIMIT :result_limit OFFSET :result_offset", plan.sql)
self.assertNotIn("North%' OR TRUE --", plan.sql)
self.assertIn("North", str(plan.parameters["filter_0"]))
calculated_only = compile_postgres_query(
dataset,
semantic,
ReportQuery(
mode="summary",
dimensions=["region"],
measures=["value_per_case"],
),
)
self.assertIn('SUM(NULLIF(source_row ->> :measure_0, \'\')::numeric)', calculated_only.sql)
self.assertIn('AS "value_per_case"', calculated_only.sql)
def test_files_publication_is_idempotent_and_retains_evidence(self) -> None:
self._create_report_graph()
registry = CapabilityRegistry()
store = ArtifactStore()
registry.providers[CAPABILITY_FILES_ARTIFACT_STORE] = store
registry.providers[CAPABILITY_REPORTING_PUBLICATION_FILES] = (
FilesReportingPublicationTarget(registry)
)
execution = execute_report(
self.session,
self.principal,
registry=registry,
report_id="report-1",
report_revision=1,
parameters={},
query=None,
idempotency_key="publish-source",
)
first = publish_execution(
self.session,
self.principal,
registry=registry,
execution_id=str(execution["execution_id"]),
target_capability=CAPABILITY_REPORTING_PUBLICATION_FILES,
target_ref="Reports/Monthly",
format="csv",
idempotency_key="publish-files-once",
options={"filename": "regional workload.csv"},
)
replay = publish_execution(
self.session,
self.principal,
registry=registry,
execution_id=str(execution["execution_id"]),
target_capability=CAPABILITY_REPORTING_PUBLICATION_FILES,
target_ref="Reports/Monthly",
format="csv",
idempotency_key="publish-files-once",
options={"filename": "regional workload.csv"},
)
self.assertEqual(first["publication_id"], replay["publication_id"])
self.assertEqual(1, len(store.requests))
self.assertEqual("version-1", first["evidence"]["file_version_id"])
self.assertEqual(
1,
len(
list_publications(
self.session,
self.principal,
execution_id=str(execution["execution_id"]),
)
),
)
targets = publication_target_catalog(registry)
self.assertTrue(targets[0]["available"])
self.assertFalse(targets[1]["available"])
self.assertIn("Enable Mail", str(targets[1]["reason"]))
def _create_report_graph(
self,
*,