feat(dataflow): add governed DSAR coverage

This commit is contained in:
2026-08-21 03:26:42 +02:00
parent d12e0bce7d
commit 6767905cbb
5 changed files with 1464 additions and 13 deletions
+67 -13
View File
@@ -17,6 +17,7 @@ from govoplan_core.core.dataflows import (
CAPABILITY_DATAFLOW_TRIGGER_DISPATCHER,
)
from govoplan_core.core.modules import (
CapabilityDocumentation,
DocumentationTopic,
FrontendModule,
FrontendRoute,
@@ -47,6 +48,10 @@ from govoplan_core.core.search import SearchSourceProviderRegistration
from govoplan_core.core.views import ViewSurface
from govoplan_core.db.base import Base
from govoplan_dataflow.backend.db import models as dataflow_models
from govoplan_dataflow.backend.dsar_provider import (
DATAFLOW_DSAR_CAPABILITY,
DataflowDsarProvider,
)
MODULE_ID = "dataflow"
@@ -142,6 +147,27 @@ ROLE_TEMPLATES = (
)
DOCUMENTATION = (
DocumentationTopic(
id="dataflow.data-subject-requests",
title="Dataflow data-subject requests",
summary="Minimize retained transformation detail without treating derived flows as authoritative subject records.",
body=(
"Dataflow matches exact tenant-scoped pipeline, revision, reconciliation, run, deployment, trigger, and delivery identifiers plus minimized account, identity, and membership attribution. Results never copy graphs, SQL, request or event payloads, reconciliation corrections, authorization snapshots, provenance bodies, errors, source details, hashes, credentials, or output rows. Dataflow does not scan arbitrary transformation content for a person; the authoritative input module must locate and correct subject facts. "
"Explicitly identified terminal run and delivery detail can be minimized idempotently, and automation authority linked to the subject can be disabled and revoked. Definitions, reconciliation evidence, active work, deployments, broad pipeline packages, published Datasource outputs, and institutional attribution require authorized review or retention. Correct sources and refresh Datasource, Search, and Reporting derivatives after review."
),
layer="configured",
documentation_types=("admin", "user"),
audience=("user", "operator", "module_admin", "data_steward", "auditor"),
order=74,
related_modules=("core", "datasources", "reporting", "workflow_engine"),
metadata={
"help_contexts": [
"dataflow.data-subject-requests",
"dataflow.runs",
"dataflow.triggers",
],
},
),
DocumentationTopic(
id="dataflow.module-boundary",
title="Dataflow module boundary",
@@ -248,7 +274,13 @@ DOCUMENTATION = (
documentation_types=("admin", "user"),
audience=("operator", "module_admin", "power_user", "product_owner"),
order=77,
related_modules=("datasources", "workflow_engine", "notifications", "policy", "audit"),
related_modules=(
"datasources",
"workflow_engine",
"notifications",
"policy",
"audit",
),
metadata={
"help_contexts": [
"dataflow.field.scope",
@@ -323,6 +355,11 @@ def _run_provider(context: ModuleContext):
return SqlDataflowRunLifecycleProvider(registry=context.registry)
def _dsar_provider(context: ModuleContext) -> DataflowDsarProvider:
del context
return DataflowDsarProvider()
def _run_worker(context: ModuleContext):
from govoplan_dataflow.backend.run_worker import SqlDataflowRunWorker
@@ -367,25 +404,20 @@ def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
),
"dataflow_trigger_deliveries": (
session.query(dataflow_models.DataflowTriggerDelivery)
.filter(
dataflow_models.DataflowTriggerDelivery.tenant_id
== tenant_id
)
.filter(dataflow_models.DataflowTriggerDelivery.tenant_id == tenant_id)
.count()
),
"dataflow_reconciliation_decision_sets": (
session.query(dataflow_models.DataflowReconciliationDecisionSet)
.filter(
dataflow_models.DataflowReconciliationDecisionSet.tenant_id
== tenant_id
dataflow_models.DataflowReconciliationDecisionSet.tenant_id == tenant_id
)
.count()
),
"dataflow_reconciliation_decisions": (
session.query(dataflow_models.DataflowReconciliationDecision)
.filter(
dataflow_models.DataflowReconciliationDecision.tenant_id
== tenant_id
dataflow_models.DataflowReconciliationDecision.tenant_id == tenant_id
)
.count()
),
@@ -420,15 +452,22 @@ manifest = ModuleManifest(
CAPABILITY_POLICY_DEFINITION_GOVERNANCE,
),
provides_interfaces=(
ModuleInterfaceProvider(name="dataflow.pipeline_catalog", version=MODULE_VERSION),
ModuleInterfaceProvider(name="dataflow.pipeline_preview", version=MODULE_VERSION),
ModuleInterfaceProvider(
name="dataflow.pipeline_catalog", version=MODULE_VERSION
),
ModuleInterfaceProvider(
name="dataflow.pipeline_preview", version=MODULE_VERSION
),
ModuleInterfaceProvider(name="dataflow.run_lifecycle", version=MODULE_VERSION),
ModuleInterfaceProvider(name="dataflow.run_worker", version=MODULE_VERSION),
ModuleInterfaceProvider(name=CAPABILITY_DATAFLOW_DATASET_OUTPUT, version=MODULE_VERSION),
ModuleInterfaceProvider(
name=CAPABILITY_DATAFLOW_DATASET_OUTPUT, version=MODULE_VERSION
),
ModuleInterfaceProvider(
name="dataflow.trigger_dispatcher",
version=MODULE_VERSION,
),
ModuleInterfaceProvider(name=DATAFLOW_DSAR_CAPABILITY, version="0.1.0"),
),
requires_interfaces=(
ModuleInterfaceRequirement(
@@ -612,6 +651,16 @@ manifest = ModuleManifest(
CAPABILITY_DATAFLOW_RUN_LIFECYCLE: _run_provider,
CAPABILITY_DATAFLOW_RUN_WORKER: _run_worker,
CAPABILITY_DATAFLOW_TRIGGER_DISPATCHER: _trigger_provider,
DATAFLOW_DSAR_CAPABILITY: _dsar_provider,
},
capability_documentation={
DATAFLOW_DSAR_CAPABILITY: CapabilityDocumentation(
label="Dataflow data-subject request provider",
summary="Finds and minimizes subject-linked transformation state and automation authority.",
contract_version="0.1.0",
documentation_types=("admin", "user"),
audience=("privacy_officer", "data_steward", "user"),
),
},
search_sources=(
SearchSourceProviderRegistration(
@@ -671,7 +720,12 @@ manifest = ModuleManifest(
"reconciliation decision set",
"transformation graph",
),
non_owned_concepts=("datasource binding", "connector transport", "report presentation", "workflow task"),
non_owned_concepts=(
"datasource binding",
"connector transport",
"report presentation",
"workflow task",
),
recovery_docs=("README.md", "docs/DURABLE_RUN_RECOVERY.md"),
security_docs=("README.md",),
operations_docs=("README.md",),