feat: implement governed datasource catalogue metadata

This commit is contained in:
2026-08-01 17:48:28 +02:00
parent 8da8035693
commit 98d93e707f
14 changed files with 1382 additions and 12 deletions
+106
View File
@@ -11,6 +11,7 @@ from govoplan_core.core.datasources import (
CAPABILITY_DATASOURCE_ORIGINS,
DatasourceAccessError,
DatasourceField,
DatasourceGovernance,
DatasourceOrigin,
DatasourceOriginReadRequest,
DatasourceOriginReadResult,
@@ -22,6 +23,7 @@ from govoplan_core.core.datasources import (
)
from govoplan_core.db.base import Base, utcnow
from govoplan_datasources.backend.db.models import (
DatasourceGovernanceReferenceRecord,
DatasourceMaterializationRecord,
DatasourcePayloadRecord,
DatasourcePayloadRowRecord,
@@ -146,6 +148,7 @@ class DatasourceLifecycleTests(unittest.TestCase):
self.engine,
tables=[
DatasourceRecord.__table__,
DatasourceGovernanceReferenceRecord.__table__,
DatasourcePayloadRecord.__table__,
DatasourcePayloadRowRecord.__table__,
DatasourceMaterializationRecord.__table__,
@@ -170,6 +173,7 @@ class DatasourceLifecycleTests(unittest.TestCase):
DatasourceMaterializationRecord.__table__,
DatasourcePayloadRowRecord.__table__,
DatasourcePayloadRecord.__table__,
DatasourceGovernanceReferenceRecord.__table__,
DatasourceRecord.__table__,
],
)
@@ -261,6 +265,108 @@ class DatasourceLifecycleTests(unittest.TestCase):
self.assertEqual(first_record.payload_id, frozen_record.payload_id)
self.assertEqual([], first_record.rows)
def test_governance_is_queryable_and_snapshotted_per_materialization(self) -> None:
stage = self.provider.create_stage(
self.session,
principal(),
stage=DatasourceStageInput(
name="Governed register",
source_name="governed_register",
kind="upload",
mode="static",
shape="tabular",
rows=({"id": 1},),
governance=DatasourceGovernance(
owner_ref="function:data-owner",
steward_ref="account:steward",
responsible_organization_ref="organization:office-1",
authority_mode="native_authoritative",
legal_basis_refs=("policy:register-use",),
purposes=("case_processing",),
semantic_definition="Authoritative case register export.",
official_keys=("id",),
classification="restricted",
publication_state="internal",
quality_policy={"required_keys": ["id"]},
affected_refs=("service:permit", "report:monthly"),
dependency_refs=("dataflow:monthly-case-check",),
),
),
)
datasource, first = self.provider.promote_stage(
self.session,
principal(),
stage_ref=stage.ref,
)
self.assertEqual("function:data-owner", datasource.governance.owner_ref)
self.assertEqual("restricted", first.governance.classification)
self.assertEqual(
[datasource.ref],
[
item.ref
for item in self.provider.list_datasources(
self.session,
principal(),
authority_mode="native_authoritative",
classification="restricted",
publication_state="internal",
owner_ref="function:data-owner",
responsible_organization_ref="organization:office-1",
)
],
)
self.assertEqual(
[datasource.ref],
[
item.ref
for item in self.provider.list_datasources(
self.session,
principal(),
affected_ref="service:permit",
dependency_ref="dataflow:monthly-case-check",
)
],
)
self.assertEqual(
(),
self.provider.list_datasources(
self.session,
principal("tenant-2"),
affected_ref="service:permit",
),
)
changed = self.provider.update_datasource_governance(
self.session,
principal(),
datasource_ref=datasource.ref,
governance=DatasourceGovernance(
owner_ref="function:new-owner",
authority_mode="native_authoritative",
purposes=("case_processing",),
classification="confidential",
publication_state="internal",
),
)
history = self.provider.list_materializations(
self.session,
principal(),
datasource_ref=datasource.ref,
)
self.assertEqual("function:new-owner", changed.governance.owner_ref)
self.assertEqual("function:data-owner", history[0].governance.owner_ref)
self.assertEqual("restricted", history[0].governance.classification)
self.assertEqual(
(),
self.provider.list_datasources(
self.session,
principal(),
dependency_ref="dataflow:monthly-case-check",
),
)
def test_live_reads_origin_and_cached_refresh_is_explicit(self) -> None:
live = self.provider.register_origin(
self.session,