3 Commits
Author SHA1 Message Date
zemion 65609665a2 feat(connectors): document consequential governance actions
Module Package Release / publish-packages (push) Successful in 12s
2026-08-24 11:36:36 +02:00
zemion a51d3781a9 docs: complete German structured documentation
Module Package Release / publish-packages (push) Successful in 11s
2026-08-24 01:15:32 +02:00
zemion d86894b321 docs(connectors): complete German reference coverage
Module Package Release / publish-packages (push) Successful in 13s
2026-08-23 21:09:35 +02:00
9 changed files with 236 additions and 13 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "govoplan-connectors" name = "govoplan-connectors"
version = "0.1.22" version = "0.1.25"
description = "Governed connector catalogue and tabular source capabilities for GovOPlaN." description = "Governed connector catalogue and tabular source capabilities for GovOPlaN."
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
@@ -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)
@@ -0,0 +1,77 @@
"""German translations for public structured documentation metadata."""
from __future__ import annotations
from typing import Any
GERMAN_STRUCTURED_TRANSLATIONS: dict[str, dict[str, Any]] = {'connectors.data-subject-requests': {'consequence_classes': {'exclude_connector_secrets': 'Gibt '
'niemals '
'Anmeldeinformationen, '
'Endpunkte, '
'externe '
'Zeilen '
'oder '
'Beweisnutzlasten '
'zurück.',
'export_operator_attribution': 'Retouren '
'minimierte '
'Connector-Aktivität '
'für '
'das '
'genaue '
'Konto.',
'retain_connector_evidence': 'Bewahrt '
'die '
'Konfiguration '
'und '
'die '
'Rechenschaftspflicht '
'für '
'externe '
'Operationen '
'bei.'}},
'connectors.governed-configuration': {'outcome': 'Das aktive Konnektorverhalten ist inspizierbar, '
'versionengebunden, testbar und überprüfbar, '
'bevor ein anbieterspezifisches Schreiben '
'erfolgt.',
'prerequisites': ['Eine Konnektordefinition wurde '
'installiert oder erstellt.',
'Anmeldematerial wird außerhalb der '
'Connector-URL gespeichert und durch eine '
'genehmigte geheime Kennung '
'referenziert.'],
'verification': 'Laden Sie die Konfiguration neu, prüfen '
'Sie geschützte Pfade und effektiven Hash, '
'führen Sie eine Simulation mit einem neuen '
'idempotency-Schlüssel aus und lösen Sie '
'alle ausstehenden Überprüfungsergebnisse.'},
'connectors.mediawiki-bluespice': {'outcome': 'Externes Wissen bleibt identitätsstabil, '
'verlustsichtbar, ACL-sicher und migrationsfähig.',
'prerequisites': ['Eine aktive verwaltete MediaWiki Action '
'API-Konfiguration existiert.',
'Namespace-Ziele und Fallback-ACLs wurden '
'überprüft.',
'Die Such- und Wiki-Module sind optional und '
'bleiben fähigkeitsgetrennt.'],
'verification': 'Entdecken Sie das Profil neu, führen Sie ein '
'Delta mit Schlüsseln aus, inspizieren Sie '
'Gesundheit und Diagnose, überprüfen Sie einen '
'erlaubten und verweigerten Suchprinzipal und '
'führen Sie einen Migrations-Dry-Run aus, '
'bevor Sie zielseitig arbeiten.'},
'connectors.znuny-otrs': {'outcome': 'Externe Tickets bleiben identitätsstabil, verlustsichtbar, '
'ACL-sicher, wiederherstellbar und semantisch getrennt von '
'GovOPlaN-Domäneneinträgen.',
'prerequisites': ['Eine aktiv gesteuerte Znuny/OTRS GenericInterface '
'REST-Konfiguration existiert.',
'Bereitstellungsdefinierte Routen, '
'Warteschlangenpartitionen, Autorität und '
'Fallback-ACLs wurden überprüft.',
'Tickets, Helpdesk, Cases und Search bleiben '
'optionale funktionsgetrennte Verbraucher.'],
'verification': 'Entdecke das Profil neu, beende einen Keyed-Full-Run, '
'führe ein Keyed-Delta aus, inspiziere die '
'Mapping-Diagnose, überprüfe einen erlaubten und '
'verweigerten Suchprinzipal und versöhne jedes '
'ergebnisunbekannte Update vor dem erneuten Versuch.'}}
+54 -4
View File
@@ -1,5 +1,8 @@
from __future__ import annotations from __future__ import annotations
from govoplan_core.core.modules import with_documentation_structured_translations
from govoplan_connectors.backend.german_structured_documentation import GERMAN_STRUCTURED_TRANSLATIONS
from pathlib import Path from pathlib import Path
from govoplan_core.core.access import ( from govoplan_core.core.access import (
@@ -14,6 +17,7 @@ from govoplan_core.core.datasources import CAPABILITY_DATASOURCE_ORIGINS
from govoplan_core.core.feeds import CAPABILITY_CONNECTORS_FEEDS from govoplan_core.core.feeds import CAPABILITY_CONNECTORS_FEEDS
from govoplan_core.core.modules import ( from govoplan_core.core.modules import (
CapabilityDocumentation, CapabilityDocumentation,
DocumentationCondition,
DocumentationTopic, DocumentationTopic,
FrontendModule, FrontendModule,
MigrationSpec, MigrationSpec,
@@ -116,10 +120,13 @@ from govoplan_connectors.backend.provider_state import (
service_desk_provider_states, service_desk_provider_states,
tabular_provider_states, tabular_provider_states,
) )
from govoplan_connectors.backend.german_documentation import (
localize_documentation_topics,
)
MODULE_ID = "connectors" MODULE_ID = "connectors"
MODULE_VERSION = "0.1.22" MODULE_VERSION = "0.1.25"
TABULAR_SOURCE_INTERFACE_VERSION = "0.1.0" TABULAR_SOURCE_INTERFACE_VERSION = "0.1.0"
DATASOURCE_ORIGIN_INTERFACE_VERSION = "0.1.0" DATASOURCE_ORIGIN_INTERFACE_VERSION = "0.1.0"
SANCTIONS_SNAPSHOT_INTERFACE_VERSION = "1.0.0" SANCTIONS_SNAPSHOT_INTERFACE_VERSION = "1.0.0"
@@ -959,7 +966,7 @@ manifest = ModuleManifest(
label="Connectors", label="Connectors",
), ),
), ),
documentation=( documentation=localize_documentation_topics((
DocumentationTopic( DocumentationTopic(
id="connectors.data-subject-requests", id="connectors.data-subject-requests",
title="Connector data-subject requests", title="Connector data-subject requests",
@@ -1014,7 +1021,7 @@ manifest = ModuleManifest(
related_modules=("policy", "audit", "dataflow", "ops"), related_modules=("policy", "audit", "dataflow", "ops"),
order=38, order=38,
metadata={ metadata={
"kind": "guide", "kind": "workflow",
"help_contexts": ["connectors.admin.governed-configurations"], "help_contexts": ["connectors.admin.governed-configurations"],
"prerequisites": [ "prerequisites": [
"A connector definition has been installed or authored.", "A connector definition has been installed or authored.",
@@ -1023,6 +1030,12 @@ manifest = ModuleManifest(
"outcome": "The active connector behavior is inspectable, version-pinned, testable, and reviewable before any provider-specific write.", "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.", "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( DocumentationTopic(
id="connectors.authority-and-effects", id="connectors.authority-and-effects",
@@ -1038,6 +1051,38 @@ manifest = ModuleManifest(
audience=("operator", "module_admin", "power_user", "product_owner"), audience=("operator", "module_admin", "power_user", "product_owner"),
related_modules=("datasources", "dataflow", "ops", "policy", "audit"), related_modules=("datasources", "dataflow", "ops", "policy", "audit"),
order=39, order=39,
metadata={
"kind": "reference",
"help_contexts": [
"connectors.action.reject-ambiguous-result",
"connectors.action.approve-ambiguous-result",
],
"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( DocumentationTopic(
id="connectors.runtime-preview-contract", id="connectors.runtime-preview-contract",
@@ -1218,7 +1263,12 @@ 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.", "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.",
}, },
), ),
), )),
)
manifest = with_documentation_structured_translations(
manifest, locale="de", translations=GERMAN_STRUCTURED_TRANSLATIONS
) )
+20
View File
@@ -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")
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@govoplan/connectors-webui", "name": "@govoplan/connectors-webui",
"version": "0.1.22", "version": "0.1.25",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
@@ -483,7 +483,7 @@ export default function ConnectorGovernancePage({ settings, auth }: Props) {
<FormField label="Endpoint URL" hint="Credentials are rejected in URLs."> <FormField label="Endpoint URL" hint="Credentials are rejected in URLs.">
<input value={draft.endpoint_url} disabled={!canAdmin || busy} placeholder="https://provider.example/api" onChange={(event) => setDraft({ ...draft, endpoint_url: event.target.value })} /> <input value={draft.endpoint_url} disabled={!canAdmin || busy} placeholder="https://provider.example/api" onChange={(event) => setDraft({ ...draft, endpoint_url: event.target.value })} />
</FormField> </FormField>
<FormField label="Credential reference" hint="Reference an approved secret; do not paste a secret."> <FormField label="Credential reference" hint="Reference an approved secret; do not paste a secret." helpContextId="connectors.admin.governed-configurations" helpModuleId="connectors">
<input value={draft.credential_ref} disabled={!canAdmin || busy} placeholder="vault://connectors/provider" onChange={(event) => setDraft({ ...draft, credential_ref: event.target.value })} /> <input value={draft.credential_ref} disabled={!canAdmin || busy} placeholder="vault://connectors/provider" onChange={(event) => setDraft({ ...draft, credential_ref: event.target.value })} />
</FormField> </FormField>
<FormField label="Ambiguous-result policy"> <FormField label="Ambiguous-result policy">
@@ -574,7 +574,7 @@ export default function ConnectorGovernancePage({ settings, auth }: Props) {
<FormField label="Endpoint URL"> <FormField label="Endpoint URL">
<input value={newDraft.endpoint_url} disabled={busy} onChange={(event) => setNewDraft({ ...newDraft, endpoint_url: event.target.value })} /> <input value={newDraft.endpoint_url} disabled={busy} onChange={(event) => setNewDraft({ ...newDraft, endpoint_url: event.target.value })} />
</FormField> </FormField>
<FormField label="Credential reference"> <FormField label="Credential reference" helpContextId="connectors.admin.governed-configurations" helpModuleId="connectors">
<input value={newDraft.credential_ref} disabled={busy} onChange={(event) => setNewDraft({ ...newDraft, credential_ref: event.target.value })} /> <input value={newDraft.credential_ref} disabled={busy} onChange={(event) => setNewDraft({ ...newDraft, credential_ref: event.target.value })} />
</FormField> </FormField>
<FormField label="Ambiguous-result policy"> <FormField label="Ambiguous-result policy">
@@ -597,8 +597,8 @@ export default function ConnectorGovernancePage({ settings, auth }: Props) {
closeDisabled={busy} closeDisabled={busy}
footer={<> footer={<>
<Button onClick={() => setReviewRun(null)} disabled={busy}>Cancel</Button> <Button onClick={() => setReviewRun(null)} disabled={busy}>Cancel</Button>
<Button variant="danger" onClick={() => void decideReview("rejected")} disabled={busy || reviewReason.trim().length < 5}>Reject</Button> <Button variant="danger" helpContextId="connectors.action.reject-ambiguous-result" helpModuleId="connectors" onClick={() => void decideReview("rejected")} disabled={busy || reviewReason.trim().length < 5}>Reject</Button>
<Button variant="primary" onClick={() => void decideReview("approved")} disabled={busy || reviewReason.trim().length < 5}>Approve</Button> <Button variant="primary" helpContextId="connectors.action.approve-ambiguous-result" helpModuleId="connectors" onClick={() => void decideReview("approved")} disabled={busy || reviewReason.trim().length < 5}>Approve</Button>
</>} </>}
> >
<p>Review {reviewRun?.summary.ambiguous ?? 0} ambiguous effects against the retained input and configuration hashes before deciding.</p> <p>Review {reviewRun?.summary.ambiguous ?? 0} ambiguous effects against the retained input and configuration hashes before deciding.</p>
+2 -2
View File
@@ -326,7 +326,7 @@ export default function ExternalKnowledgePage({ settings, auth }: Props) {
<Button variant="secondary" onClick={() => void sync(false)} disabled={!selected || !canSync || busy || dirty}>Run delta</Button> <Button variant="secondary" onClick={() => void sync(false)} disabled={!selected || !canSync || busy || dirty}>Run delta</Button>
<Button variant="secondary" onClick={() => void sync(true)} disabled={!selected || !canSync || busy || dirty}>Run full backfill</Button> <Button variant="secondary" onClick={() => void sync(true)} disabled={!selected || !canSync || busy || dirty}>Run full backfill</Button>
<Button variant="secondary" onClick={() => setMigrationOpen(true)} disabled={!selected || !canMigrate || busy || dirty}>Preview migration</Button> <Button variant="secondary" onClick={() => setMigrationOpen(true)} disabled={!selected || !canMigrate || busy || dirty}>Preview migration</Button>
<Button variant="primary" onClick={() => setPublishOpen(true)} disabled={!selected || !canPublish || busy || dirty}>Publish page</Button> <Button variant="primary" helpContextId="connectors.admin.external-knowledge" helpModuleId="connectors" onClick={() => setPublishOpen(true)} disabled={!selected || !canPublish || busy || dirty}>Publish page</Button>
</>} </>}
discardAction={{ discardAction={{
label: "Discard changes", label: "Discard changes",
@@ -501,7 +501,7 @@ export default function ExternalKnowledgePage({ settings, auth }: Props) {
<Dialog open={publishOpen} title="Publish provider page revision" onClose={() => !busy && setPublishOpen(false)} closeDisabled={busy} footer={<> <Dialog open={publishOpen} title="Publish provider page revision" onClose={() => !busy && setPublishOpen(false)} closeDisabled={busy} footer={<>
<Button onClick={() => setPublishOpen(false)} disabled={busy}>Cancel</Button> <Button onClick={() => setPublishOpen(false)} disabled={busy}>Cancel</Button>
<Button variant="primary" onClick={() => void publish()} disabled={busy || !externalPageId.trim() || !publishTitle.trim()}>Publish revision</Button> <Button variant="primary" helpContextId="connectors.admin.external-knowledge" helpModuleId="connectors" onClick={() => void publish()} disabled={busy || !externalPageId.trim() || !publishTitle.trim()}>Publish revision</Button>
</>}> </>}>
<p className="muted">Publication is an external effect. Supply the current provider revision where possible; an unknown outcome blocks blind retry.</p> <p className="muted">Publication is an external effect. Supply the current provider revision where possible; an unknown outcome blocks blind retry.</p>
<FormGrid columns={2} collapseAt="standard" className=""> <FormGrid columns={2} collapseAt="standard" className="">
+1 -1
View File
@@ -72,7 +72,7 @@ const adminSections: AdminSectionsUiCapability = {
export const connectorsModule: PlatformWebModule = { export const connectorsModule: PlatformWebModule = {
id: "connectors", id: "connectors",
label: "Connectors", label: "Connectors",
version: "0.1.22", version: "0.1.23",
dependencies: [], dependencies: [],
optionalDependencies: ["access", "audit", "policy", "ops", "search", "wiki", "tickets", "helpdesk", "cases"], optionalDependencies: ["access", "audit", "policy", "ops", "search", "wiki", "tickets", "helpdesk", "cases"],
viewSurfaces: [ viewSurfaces: [