diff --git a/pyproject.toml b/pyproject.toml index beb26c3..c263650 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "govoplan-connectors" -version = "0.1.22" +version = "0.1.23" description = "Governed connector catalogue and tabular source capabilities for GovOPlaN." readme = "README.md" requires-python = ">=3.12" diff --git a/src/govoplan_connectors/backend/german_documentation.py b/src/govoplan_connectors/backend/german_documentation.py new file mode 100644 index 0000000..cd44ac2 --- /dev/null +++ b/src/govoplan_connectors/backend/german_documentation.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from dataclasses import replace +from typing import Iterable + +from govoplan_core.core.modules import DocumentationTopic + + +_TRANSLATIONS = { + "connectors.data-subject-requests": { + "title": "Datenschutzanfragen zu Connector-Daten", + "summary": "Nachvollziehbare Connector-Aktivitäten exportieren, ohne Zugangsdaten oder externe Inhalte offenzulegen.", + "body": ( + "Connectors gleicht ausschließlich eine eindeutige, mandantenbezogene Konto-ID ab und kann eine bereits verifizierte Suche auf eine Quelle, einen Abruf, eine Definition, Konfiguration, Simulation, ein externes Wissens- oder Service-Desk-Profil oder einen Connector-Vorgang begrenzen. Der Export weist Konfigurations-, Abruf-, Simulations-, externe Vorgangs- und Prüfaktivitäten anhand begrenzter Lebenszyklusmetadaten aus. Zugangsdaten, Endpunktreferenzen, Quelldatensätze, externe Antworten, Anfrageinhalte, Mapping- oder Konfigurationsdokumente, Diagnosen, Provenienz, Hashwerte und Transportnachweise werden nie ausgegeben. Die Zuordnung zu handelnden Personen bleibt unveränderlicher Governance- und Vorgangsnachweis und wird nicht automatisch gelöscht. E-Mail- oder Objektkennungen ohne verifizierte Konto-ID begründen keinen Treffer." + ), + }, + "connectors.governed-configuration": { + "title": "Connector-Definitionen und Simulationen steuern", + "summary": "Connector-Schemata und Mappings versionieren und dabei lokale Überschreibungen sowie Prüfnachweise erhalten.", + "body": ( + "Connector-Administrationen erstellen paketverwaltete oder lokale Definitionen, die Anbieter, Protokoll, Fähigkeiten, Schemata, Mapping-Regeln, Validierung, Vorschau, Audit-Anforderungen, Datenschutz, Aufbewahrung, Grenzen und Wiederholungsverhalten ausdrücklich beschreiben. Jede Änderung erzeugt eine unveränderliche Revision. Eine Mandantenkonfiguration bindet genau eine Revision und speichert nur eine Zugangsdatenreferenz; Paketaktualisierungen ändern die wirksame Konfiguration erst nach bewusster Übernahme. Lokale Überschreibungen werden als geschützt angezeigt und bei der Übernahme erneut angewendet. Testläufe und Simulationen sind begrenzt, schwärzen konfigurierte Felder, sind über den Aufrufschlüssel idempotent und bewahren Konfigurations-, Mapping-, Eingabe- und externe Revisionsprovenienz. Mehrdeutige Ergebnisse folgen der Richtlinie für manuelle Prüfung, Quarantäne oder Ablehnung. Offene und quarantänisierte Nachweise müssen mit Begründung freigegeben oder abgelehnt werden. Eine erfolgreiche generische Simulation verspricht keinen anbieterspezifischen Live-Schreibzugriff." + ), + }, + "connectors.authority-and-effects": { + "title": "Autorität und Wirkungen eines Connectors verstehen", + "summary": "Richtung, technische Reife und konfigurierte Quellenautorität getrennt und sichtbar behandeln.", + "body": ( + "Ein Connector kann Daten beziehen, veröffentlichen oder bidirektional arbeiten und von reiner Erkennung bis zum vollständigen Ersatz reifen. Jede Bindung legt getrennt fest, ob GovOPlaN führend ist, einer externen Autorität folgt, einen Spiegel führt, nach Konfliktregeln synchronisiert, eine Governance-Schicht ergänzt oder nur einen Verweis bewahrt. Schreibende Anbieter müssen Revisionen, Grenzen, Idempotenz, unbekannte Ergebnisse, Nachweise, Abgleich, Korrektur, Ausfallverhalten und Anforderungen an Geheimnisse erläutern." + ), + }, + "connectors.runtime-preview-contract": { + "title": "Connector-Vorschauen und Diagnosen", + "summary": "Für externe Transporte ein einheitliches, begrenztes und geschwärztes Testlaufformat verwenden.", + "body": ( + "Connectors verantwortet Endpunkterkennung, Authentifizierungsübergabe, Transportgrenzen, Wiederholungen und Protokollzustand. Fachmodule verantworten Feldzuordnung, Validierung, Abgleich und Datensatzänderungen. Der gemeinsame Core-Laufzeitvertrag meldet geschwärzte Wirkungen und Diagnosen zusammen mit Quellrevisionen, Fingerabdrücken und unveränderlichen Eingabe-Hashes. Tabellarische Vorschauen begrenzen Zeilen, serialisierte Bytes und Laufzeit und melden Abschneidungen strukturiert. Eine Übernahme muss veraltete, abgeschnittene, widersprüchliche oder fehlerhafte Vorschauen ablehnen; Zugangsdaten erscheinen weder in URLs noch in Beispielen." + ), + }, + "connectors.tabular-sources": { + "title": "Gesteuerte tabellarische Quellen", + "summary": "Anbieterneutrale Quellenerkennung und begrenzte Lesezugriffe für Dataflow bereitstellen.", + "body": ( + "Connectors verantwortet Quellkonfiguration, Zugriffsprüfung, Schemaerkennung, Fingerabdrücke und begrenzte Lesevorgänge. Dataflow speichert nur undurchsichtige Quellreferenzen und erwartete Fingerabdrücke. Jede Quelle weist ihren Live-, Cache-, Datei- oder statischen Modus, einen strukturierten Zustand und unterstützte Projektion, Filterung, Aggregation, Sortierung und Seitennavigation aus. Unveränderliche JSON- und CSV-Snapshots bleiben verfügbar. Verwaltete CSV- und XLSX-Quellen nutzen optional Files, binden eine exakt autorisierte Version, erzwingen Archiv- und Entpackgrenzen und übernehmen neuere Versionen erst nach ausdrücklicher Aktualisierung. Der PostgreSQL-Adapter nutzt eine aktive gesteuerte Konfiguration und eine eingegrenzte Core-Zugangsdatenhülle, liest nur einfache Schema- und Tabellenkennungen und blockiert bei Konfigurations-, Zugangsdaten- oder Schemadrift bis zur geprüften Aktualisierung. Zugangsdaten, Endpunkte, Speicherschlüssel und interne Dateiinhalte werden nie über die Quelle offengelegt." + ), + }, + "connectors.sanctions-snapshots": { + "title": "Snapshots von Sanktionsquellen", + "summary": "Unveränderliche, per Prüfsumme verifizierbare Sanktionslistennachweise abrufen, ohne Prüfsachverhalte zu übertragen.", + "body": ( + "Connectors stellt eine deterministische synthetische Testquelle und die offizielle konsolidierte XML-Liste des Sicherheitsrats der Vereinten Nationen bereit. Jeder Abruf zeichnet bedingte Transportnachweise, begrenzte Wiederholungen, Zustand, Quellmetadaten, Rohbeleg und SHA-256-Prüfsumme auf. Vor Anbieterzugriffen erwirbt die Aktualisierung eine verteilte Recovery-Sperre; unveränderlicher Snapshot und abschließender Recovery-Prüfpunkt werden anschließend in einer Transaktion gespeichert. Derselbe Anfrageschlüssel liefert dasselbe Ergebnis. Risk Compliance verantwortet Normalisierung, Abgleich, rechtliche Prüfung und Entscheidungen." + ), + }, + "connectors.rss-atom": { + "title": "RSS- und Atom-Feeds", + "summary": "Gesteuerte Feed-Snapshots importieren und nach Sichtbarkeit gefilterte Feeds ausgeben.", + "body": ( + "Connectors verantwortet begrenzten, gegen SSRF geschützten RSS-/Atom-Transport und XML-Verarbeitung. Importierte Einträge werden zu unveränderlichen tabellarischen Snapshots in Datasources und enthalten Abruf, Aktualität, ETag, Inhaltsdigest und Quellprovenienz. Ausgaben akzeptieren nur provenienzbelegte Ereignis-, Veröffentlichungs-, Fall- oder Berichtsauswahlen einer verantwortlichen Oberfläche. Die Zielgruppe setzt die Sichtbarkeitsobergrenze: Öffentliche Feeds enthalten nur öffentliche Einträge; mandantenbezogene oder private Feeds benötigen eine eigene Berechtigung. Aufrufende Stellen dürfen keine eigene Sichtbarkeitsliste vorgeben. Portal oder Reporting verantwortet dauerhafte Veröffentlichungsrouten und autorisiert jeden Zugriff auf eingeschränkte Feeds neu. Ein eigenes RSS-Modul ist erst erforderlich, wenn GovOPlaN später eine eigenständige Feed-Reader-Oberfläche benötigt." + ), + }, +} + + +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_connectors/backend/manifest.py b/src/govoplan_connectors/backend/manifest.py index d956e32..6332223 100644 --- a/src/govoplan_connectors/backend/manifest.py +++ b/src/govoplan_connectors/backend/manifest.py @@ -14,6 +14,7 @@ from govoplan_core.core.datasources import CAPABILITY_DATASOURCE_ORIGINS from govoplan_core.core.feeds import CAPABILITY_CONNECTORS_FEEDS from govoplan_core.core.modules import ( CapabilityDocumentation, + DocumentationCondition, DocumentationTopic, FrontendModule, MigrationSpec, @@ -116,10 +117,13 @@ from govoplan_connectors.backend.provider_state import ( service_desk_provider_states, tabular_provider_states, ) +from govoplan_connectors.backend.german_documentation import ( + localize_documentation_topics, +) MODULE_ID = "connectors" -MODULE_VERSION = "0.1.22" +MODULE_VERSION = "0.1.23" TABULAR_SOURCE_INTERFACE_VERSION = "0.1.0" DATASOURCE_ORIGIN_INTERFACE_VERSION = "0.1.0" SANCTIONS_SNAPSHOT_INTERFACE_VERSION = "1.0.0" @@ -959,7 +963,7 @@ manifest = ModuleManifest( label="Connectors", ), ), - documentation=( + documentation=localize_documentation_topics(( DocumentationTopic( id="connectors.data-subject-requests", title="Connector data-subject requests", @@ -1014,7 +1018,7 @@ manifest = ModuleManifest( related_modules=("policy", "audit", "dataflow", "ops"), order=38, metadata={ - "kind": "guide", + "kind": "workflow", "help_contexts": ["connectors.admin.governed-configurations"], "prerequisites": [ "A connector definition has been installed or authored.", @@ -1023,6 +1027,12 @@ manifest = ModuleManifest( "outcome": "The active connector behavior is inspectable, version-pinned, testable, and reviewable before any provider-specific write.", "verification": "Reload the configuration, inspect protected paths and effective hash, run a simulation with a new idempotency key, and resolve any pending review result.", }, + conditions=( + DocumentationCondition( + required_modules=("connectors",), + required_scopes=(ADMIN_SCOPE,), + ), + ), ), DocumentationTopic( id="connectors.authority-and-effects", @@ -1038,6 +1048,34 @@ manifest = ModuleManifest( audience=("operator", "module_admin", "power_user", "product_owner"), related_modules=("datasources", "dataflow", "ops", "policy", "audit"), order=39, + metadata={ + "kind": "reference", + "fields": [ + "Direction and technical maturity", + "Configured source authority", + "Revision and idempotency behavior", + "Reconciliation, outage, and secret requirements", + ], + "consequences": [ + "The authority mode determines which side may change business state.", + "A writable provider must preserve evidence and reconcile unknown outcomes before retry.", + ], + }, + structured_translation_version="1", + structured_translations={ + "de": { + "fields": [ + "Richtung und technische Reife", + "Konfigurierte Quellenautorität", + "Revisions- und Idempotenzverhalten", + "Anforderungen an Abgleich, Ausfallverhalten und Geheimnisse", + ], + "consequences": [ + "Der Autoritätsmodus bestimmt, welche Seite den Fachzustand ändern darf.", + "Ein schreibender Anbieter muss Nachweise bewahren und unbekannte Ergebnisse vor einer Wiederholung abgleichen.", + ], + } + }, ), DocumentationTopic( id="connectors.runtime-preview-contract", @@ -1218,7 +1256,7 @@ manifest = ModuleManifest( "verification": "Rediscover the profile, finish a keyed full run, run a keyed delta, inspect mapping diagnostics, verify one allowed and denied Search principal, and reconcile every outcome-unknown update before retry.", }, ), - ), + )), ) diff --git a/tests/test_documentation_contract.py b/tests/test_documentation_contract.py new file mode 100644 index 0000000..8ccac5b --- /dev/null +++ b/tests/test_documentation_contract.py @@ -0,0 +1,20 @@ +from govoplan_connectors.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["connectors.governed-configuration"] + assert workflow.metadata.get("kind") == "workflow" + assert any(condition.required_scopes for condition in workflow.conditions) + + reference = topics["connectors.authority-and-effects"] + assert reference.metadata.get("kind") == "reference" + assert reference.metadata.get("fields") + assert reference.metadata.get("consequences") + assert reference.structured_translations.get("de") diff --git a/webui/package.json b/webui/package.json index 7aa9bb4..1465e9a 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/connectors-webui", - "version": "0.1.22", + "version": "0.1.23", "private": true, "type": "module", "main": "src/index.ts", diff --git a/webui/src/module.ts b/webui/src/module.ts index 6e1fa58..c311b26 100644 --- a/webui/src/module.ts +++ b/webui/src/module.ts @@ -72,7 +72,7 @@ const adminSections: AdminSectionsUiCapability = { export const connectorsModule: PlatformWebModule = { id: "connectors", label: "Connectors", - version: "0.1.22", + version: "0.1.23", dependencies: [], optionalDependencies: ["access", "audit", "policy", "ops", "search", "wiki", "tickets", "helpdesk", "cases"], viewSurfaces: [