From 08c1ecbf81dc8034fdb2d295120ec4cd192b2a2f Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Sun, 23 Aug 2026 21:09:36 +0200 Subject: [PATCH] docs(dataflow): complete German reference coverage --- pyproject.toml | 2 +- src/govoplan_dataflow/__init__.py | 2 +- .../backend/german_documentation.py | 62 +++++++++++++++++++ src/govoplan_dataflow/backend/manifest.py | 46 +++++++++++++- tests/test_documentation_contract.py | 19 ++++++ webui/package.json | 2 +- 6 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 src/govoplan_dataflow/backend/german_documentation.py create mode 100644 tests/test_documentation_contract.py diff --git a/pyproject.toml b/pyproject.toml index 3a7786c..e857dc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "govoplan-dataflow" -version = "0.1.20" +version = "0.1.21" description = "Governed graphical and SQL data pipelines for GovOPlaN." readme = "README.md" requires-python = ">=3.12" diff --git a/src/govoplan_dataflow/__init__.py b/src/govoplan_dataflow/__init__.py index 3225ed0..04071b1 100644 --- a/src/govoplan_dataflow/__init__.py +++ b/src/govoplan_dataflow/__init__.py @@ -1,3 +1,3 @@ from __future__ import annotations -__version__ = "0.1.20" +__version__ = "0.1.21" diff --git a/src/govoplan_dataflow/backend/german_documentation.py b/src/govoplan_dataflow/backend/german_documentation.py new file mode 100644 index 0000000..a405406 --- /dev/null +++ b/src/govoplan_dataflow/backend/german_documentation.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from dataclasses import replace +from typing import Iterable + +from govoplan_core.core.modules import DocumentationTopic + + +_TRANSLATIONS = { + "dataflow.data-subject-requests": { + "title": "Datenschutzanfragen zu Dataflow-Daten", + "summary": "Gespeicherte Transformationsdetails minimieren, ohne abgeleitete Datenflüsse als führende Betroffenendaten zu behandeln.", + "body": ( + "Dataflow gleicht exakte mandantenbezogene Pipeline-, Revisions-, Abgleich-, Lauf-, Bereitstellungs-, Trigger- und Zustellkennungen sowie minimierte Konto-, Identitäts- und Mitgliedschaftszuordnungen ab. Ergebnisse enthalten niemals Graphen, SQL, Anfrage- oder Ereignisinhalte, Abgleichkorrekturen, Autorisierungssnapshots, Provenienztexte, Fehler, Quelldetails, Hashwerte, Zugangsdaten oder Ausgabezeilen. Dataflow durchsucht beliebige Transformationsinhalte nicht nach Personen; das führende Eingabemodul muss Betroffenendaten auffinden und berichtigen. Eindeutig bestimmte abgeschlossene Lauf- und Zustelldetails können idempotent minimiert und personenbezogene Automatisierungsbefugnisse deaktiviert und widerrufen werden. Definitionen, Abgleichnachweise, aktive Arbeiten, Bereitstellungen, umfassende Pipeline-Pakete, veröffentlichte Datasource-Ausgaben und institutionelle Zuordnungen benötigen eine autorisierte Prüfung oder Aufbewahrung. Nach der Prüfung sind die Quellen zu berichtigen und Ableitungen in Datasources, Search und Reporting zu aktualisieren." + ), + }, + "dataflow.module-boundary": { + "title": "Modulgrenze von Dataflow", + "summary": "Versionierte tabellarische Transformationen grafisch oder mit eingeschränktem SQL bearbeiten.", + "body": ( + "Dataflow verantwortet kanonische Pipeline-Graphen, unveränderliche Revisionen, Validierung, eingeschränkte SQL-Kompilierung, Vorschau- und Laufdiagnosen sowie Herkunftsreferenzen. Datasources verantwortet den gesteuerten Katalog und Materialisierungen, Connectors den externen Abruf und Zugangsdaten, Reporting die analytische Darstellung und Exporte, Workflow die Orchestrierung und menschliche Übergaben und Risk Compliance die Sanktionsprüfung und Richtliniengrenzen. Benutzer-SQL wird in freigegebene Transformationen übersetzt und nie ungeprüft an eine Datenbank weitergereicht." + ), + }, + "dataflow.reference.nodes-and-expressions": { + "title": "Dataflow-Knoten und Ausdrücke", + "summary": "Typisierte Knoteneingaben, Ausdrücke, Schemafortschreibung und begrenzte Zwischenergebnisse verstehen.", + "body": ( + "Jeder Graphknoten definiert typisierte Eingaben, Konfiguration, Ausgabeschema und Validierungsregeln. Quellknoten binden Inline-Inhalte oder gesteuerte Datasource-Referenzen; Verknüpfungs-, Filter-, Transformations-, Qualitäts-, Abgleich-, Teilfluss- und Ausgabeknoten bleiben im kanonischen Graphen ausdrücklich sichtbar. Wiederverwendbare Teilflüsse wählen eine durch Policy erlaubte unveränderliche Fluss- oder Vorlagenrevision. Der Server löst Graph, Quell-Hash, Policy-Entscheidung und geschlossene Ein-/Ausgabeverträge auf und bindet sie; mitgelieferte Graphkopien werden ignoriert, unvereinbare Eingaben und zyklische Referenzen abgelehnt. Abgleichzeilen führen stabile Schlüssel- und Eingabe-Hashes sowie Vorher-/Nachher-Werte. Prüfentscheidungen werden als unveränderliche, mandanteneigene Entscheidungssätze gespeichert; geänderte Eingaben werden ungültig, ohne Fachdaten still umzuschreiben. Ausdrücke führen weder Host- noch Datenbankcode aus. Knoten-Vorschauen sind begrenzt, für die handelnde Person datenschutzgefiltert und werden nicht als Laufergebnis gespeichert. SQL wird in denselben Graphen kompiliert; nicht unterstützte Anweisungen erscheinen als Diagnose." + ), + }, + "dataflow.reference.fields-and-consequences": { + "title": "Dataflow-Felder und Lebenszyklusfolgen", + "summary": "Bedeutung von Geltungsbereich, Revision, Wiederverwendung, Automatisierung, Ausführung, Veröffentlichung, Promotion und Löschung.", + "body": ( + "Der Geltungsbereich bestimmt Eigentum und Policy-Vererbung. Vorlagen können abgeleitet, aber nicht ausgeführt werden; vollständige Flüsse dürfen bei wirksamer Policy geprüft, versioniert, automatisiert und ausgeführt werden. Speichern fügt eine unveränderliche Revision an. Eine bereichsbezogene Kopie bindet Quellrevision und Inhalts-Hash; eine neuere Quelle ändert sie nicht automatisch. Die geprüfte Übernahme benötigt den exakten Quell-Hash und eine Begründung, fügt eine Kopierevision an, protokolliert Policy-Entscheidung und prüfende Person und setzt die Kopie zur erneuten Aktivierung auf Entwurf. Trigger binden Revision und Autorisierungsnachweis und prüfen ihre Befugnis je Zustellung neu. „Läufe zulassen“ ist nur die Zulassungsgrenze der Definition und gewährt niemandem eine Berechtigung. Einmalige Läufe verwenden lokale Mandantenzeit; versäumte Intervalle werden je Richtlinie zu einem Lauf zusammengefasst oder übersprungen, niemals ungeprüft vollständig nachgeholt. Das Parallelitätslimit begrenzt aktive Zustellungen und erhöht keine Worker-Kapazität. Läufe erzeugen dauerhafte Befehls- und Recovery-Nachweise. Veröffentlichung erstellt eine gesteuerte Datasource-Materialisierung; Promotion wählt eine unveränderliche Revision für Staging oder Produktion aus. Abgleichentscheidungen werden mit optimistischer Nebenläufigkeit als neue Revision gespeichert und verändern die geprüfte Fachzeile nicht. Löschen verhindert künftige Nutzung, während Lauf-, Bereitstellungs-, Herkunfts-, Audit- und Recovery-Nachweise ihrer Aufbewahrung folgen." + ), + }, + "dataflow.execution-and-recovery": { + "title": "Dataflow ausführen, veröffentlichen und wiederherstellen", + "summary": "Gebundene Läufe, Umgebungsfreigaben, Ausgabeveröffentlichung, Abbruch, Abgleich und Nachweise betreiben.", + "body": ( + "Jeder Lauf ist an eine unveränderliche Revision und einen Idempotenzschlüssel gebunden. Die Warteschlange erfasst handelnde Person, Befugnis, Umgebung, Fortschritt, Abbruch, Ausgabe und Recovery-Zustand. Reine Datenbankläufe werden atomar gespeichert. Die Veröffentlichung in eine gesteuerte Datasource nutzt Vorwärts-Recovery: Ein unbekanntes Anbieterergebnis wird vor einer Wiederholung abgeglichen, damit keine Ausgabe doppelt entsteht. Staging- und Produktionsfreigaben sind ausdrücklich und schreiben keine Revision um. Das Einfrieren einer Veröffentlichung versieht exakt die unveränderliche Ausgabe mit einer dauerhaften Bezeichnung; Daten werden weder kopiert noch von Aufbewahrungs-, Hold- und Zugriffsregeln der Datasource getrennt. Artefaktbasierte und Inline-Ausgaben liefern dieselben stabilen Veröffentlichungs-, Datasource- und Materialisierungsreferenzen. Warnungen und prüfpflichtige Zustände bleiben für Workflow sichtbar. Ein Abbruch ist nach Beginn externer Arbeit nur bestmöglich; der Abschlussnachweis unterscheidet gestoppt, abgeschlossen, fehlgeschlagen und abgleichpflichtig. Vor Annahme eines geplanten, ereignisbasierten oder eingereihten Laufs wird die Modulberechtigung des Mandanten geprüft. Eine Deaktivierung stoppt neue Annahmen und überlässt bereits angenommene Läufe einer ausdrücklichen Betriebsentscheidung. Reporting darf nur einen erfolgreichen veröffentlichten Lauf binden; Dataflow prüft Befugnis und Datasource-Zugriff erneut und liest exakt die protokollierte Materialisierung ohne erneute Parametrisierung oder Ausführung." + ), + }, +} + + +def localize_documentation_topics( + topics: Iterable[DocumentationTopic], +) -> tuple[DocumentationTopic, ...]: + localized: list[DocumentationTopic] = [] + for topic in topics: + german = _TRANSLATIONS.get(topic.id) + if german is None: + localized.append(topic) + continue + translations = { + locale: dict(value) for locale, value in topic.translations.items() + } + translations["de"] = {**translations.get("de", {}), **german} + localized.append(replace(topic, translations=translations)) + return tuple(localized) diff --git a/src/govoplan_dataflow/backend/manifest.py b/src/govoplan_dataflow/backend/manifest.py index a9f256d..8a4a7b3 100644 --- a/src/govoplan_dataflow/backend/manifest.py +++ b/src/govoplan_dataflow/backend/manifest.py @@ -18,6 +18,7 @@ from govoplan_core.core.dataflows import ( ) from govoplan_core.core.modules import ( CapabilityDocumentation, + DocumentationCondition, DocumentationTopic, FrontendModule, FrontendRoute, @@ -52,11 +53,14 @@ from govoplan_dataflow.backend.dsar_provider import ( DATAFLOW_DSAR_CAPABILITY, DataflowDsarProvider, ) +from govoplan_dataflow.backend.german_documentation import ( + localize_documentation_topics, +) MODULE_ID = "dataflow" MODULE_NAME = "Dataflow" -MODULE_VERSION = "0.1.20" +MODULE_VERSION = "0.1.21" READ_SCOPE = "dataflow:pipeline:read" WRITE_SCOPE = "dataflow:pipeline:write" @@ -146,7 +150,7 @@ ROLE_TEMPLATES = ( ), ) -DOCUMENTATION = ( +DOCUMENTATION = localize_documentation_topics(( DocumentationTopic( id="dataflow.data-subject-requests", title="Dataflow data-subject requests", @@ -197,6 +201,19 @@ DOCUMENTATION = ( "audit", ), metadata={ + "kind": "workflow", + "route": "/dataflow", + "screen": "Dataflow", + "prerequisites": [ + "You may read Dataflow pipeline definitions in the active tenant.", + ], + "steps": [ + "Open Dataflow and select a pipeline or create an authorized draft.", + "Inspect the graph, immutable revision, diagnostics, and declared source references.", + "Use preview or run actions only when the effective permissions and Policy allow them.", + ], + "outcome": "The pipeline remains a governed transformation definition with explicit module boundaries and source authority.", + "verification": "Confirm the active revision, validation diagnostics, source references, and permitted actions in the Dataflow workspace.", "first_slice": ( "Inline and governed datasources, union, join, filter, deduplication, select, " "typed expressions, conversion, quality and reconciliation, reusable subflows, " @@ -218,6 +235,27 @@ DOCUMENTATION = ( "dataflow.state.read-only", ], }, + conditions=( + DocumentationCondition( + required_modules=("dataflow",), + required_scopes=(READ_SCOPE,), + ), + ), + structured_translation_version="1", + structured_translations={ + "de": { + "prerequisites": [ + "Sie dürfen Dataflow-Pipeline-Definitionen im aktiven Mandanten lesen.", + ], + "steps": [ + "Öffnen Sie Dataflow und wählen Sie eine Pipeline oder legen Sie einen autorisierten Entwurf an.", + "Prüfen Sie Graph, unveränderliche Revision, Diagnosen und ausgewiesene Quellreferenzen.", + "Verwenden Sie Vorschau- oder Laufaktionen nur, wenn wirksame Berechtigungen und Policy sie erlauben.", + ], + "outcome": "Die Pipeline bleibt eine gesteuerte Transformationsdefinition mit ausdrücklichen Modulgrenzen und Quellenautorität.", + "verification": "Prüfen Sie aktive Revision, Validierungsdiagnosen, Quellreferenzen und erlaubte Aktionen im Dataflow-Arbeitsbereich.", + } + }, ), DocumentationTopic( id="dataflow.reference.nodes-and-expressions", @@ -247,6 +285,7 @@ DOCUMENTATION = ( order=76, related_modules=("datasources", "connectors", "policy", "audit"), metadata={ + "kind": "reference", "help_contexts": [ "dataflow.field.node-name", "dataflow.field.source", @@ -296,6 +335,7 @@ DOCUMENTATION = ( "audit", ), metadata={ + "kind": "reference", "help_contexts": [ "dataflow.field.scope", "dataflow.field.definition-kind", @@ -363,7 +403,7 @@ DOCUMENTATION = ( }, }, ), -) +)) def _dataflow_router(context: ModuleContext): diff --git a/tests/test_documentation_contract.py b/tests/test_documentation_contract.py new file mode 100644 index 0000000..3d075f5 --- /dev/null +++ b/tests/test_documentation_contract.py @@ -0,0 +1,19 @@ +from govoplan_dataflow.backend.manifest import get_manifest + + +def test_static_documentation_has_complete_german_reference_copy() -> None: + for topic in get_manifest().documentation: + german = topic.translations.get("de", {}) + assert all(german.get(field, "").strip() for field in ("title", "summary", "body")), topic.id + + +def test_documentation_exposes_conditioned_workflow_and_reference() -> None: + topics = {topic.id: topic for topic in get_manifest().documentation} + workflow = topics["dataflow.module-boundary"] + assert workflow.metadata.get("kind") == "workflow" + assert any(condition.required_scopes for condition in workflow.conditions) + assert workflow.structured_translations.get("de") + + reference = topics["dataflow.reference.fields-and-consequences"] + assert reference.metadata.get("kind") == "reference" + assert reference.metadata.get("consequence_classes") diff --git a/webui/package.json b/webui/package.json index 6b973dd..324950d 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/dataflow-webui", - "version": "0.1.20", + "version": "0.1.21", "private": true, "type": "module", "main": "src/index.ts",