Files
govoplan-connectors/src/govoplan_connectors/backend/manifest.py
T
zemion 65609665a2
Module Package Release / publish-packages (push) Successful in 12s
feat(connectors): document consequential governance actions
2026-08-24 11:36:36 +02:00

1288 lines
66 KiB
Python

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 govoplan_core.core.access import (
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
)
from govoplan_core.core.module_guards import (
drop_table_retirement_provider,
persistent_table_uninstall_guard,
)
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,
ModuleInterfaceProvider,
ModuleInterfaceRequirement,
ModuleManifest,
PermissionDefinition,
RoleTemplate,
ViewSurface,
)
from govoplan_core.core.provider_governance import (
ExternalProviderDeclaration,
ExternalProviderStateProviderRegistration,
ModuleArchitectureDeclaration,
ModuleArchitectureDocumentation,
ModuleMaturityEvidence,
ProviderBehaviorDeclaration,
ProviderObjectDeclaration,
)
from govoplan_core.core.tabular_sources import (
CAPABILITY_CONNECTORS_TABULAR_SNAPSHOT_WRITER,
CAPABILITY_CONNECTORS_TABULAR_SOURCES,
)
from govoplan_core.core.files import CAPABILITY_FILES_TABULAR_CONTENT
from govoplan_core.core.sanctions import (
CAPABILITY_CONNECTORS_SANCTIONS_SNAPSHOTS,
)
from govoplan_core.core.search import SearchSourceProviderRegistration
from govoplan_core.db.base import Base
from govoplan_connectors.backend.db.models import (
ConnectorConfiguration,
ConnectorDefinition,
ConnectorDefinitionRevision,
ConnectorKnowledgeObject,
ConnectorKnowledgeProfile,
ConnectorKnowledgeSyncRun,
ConnectorServiceDeskObject,
ConnectorServiceDeskProfile,
ConnectorServiceDeskSyncRun,
ConnectorSanctionsAcquisitionRun,
ConnectorSanctionsSnapshot,
ConnectorSimulationRun,
ConnectorTabularSource,
)
from govoplan_connectors.backend.knowledge_connector import (
KNOWLEDGE_ADMIN_SCOPE,
KNOWLEDGE_CAPABILITY,
KNOWLEDGE_INTERFACE_VERSION,
KNOWLEDGE_MIGRATE_SCOPE,
KNOWLEDGE_PROVIDER_ID,
KNOWLEDGE_PUBLISH_SCOPE,
KNOWLEDGE_READ_SCOPE,
KNOWLEDGE_SYNC_SCOPE,
ExternalKnowledgeCapability,
)
from govoplan_connectors.backend.knowledge_search import (
create_external_knowledge_search_source,
)
from govoplan_connectors.backend.service_desk_connector import (
SERVICE_DESK_ADMIN_SCOPE,
SERVICE_DESK_CAPABILITY,
SERVICE_DESK_INTERFACE_VERSION,
SERVICE_DESK_PROVIDER_ID,
SERVICE_DESK_READ_SCOPE,
SERVICE_DESK_SYNC_SCOPE,
SERVICE_DESK_UPDATE_SCOPE,
ExternalServiceDeskCapability,
)
from govoplan_connectors.backend.service_desk_search import (
create_external_service_desk_search_source,
)
from govoplan_connectors.backend.dsar_provider import (
CONNECTORS_DSAR_CAPABILITY,
ConnectorsDsarProvider,
)
from govoplan_connectors.backend.sanctions_sources import (
SANCTIONS_READ_SCOPE,
SANCTIONS_REFRESH_SCOPE,
SqlSanctionsSnapshotProvider,
)
from govoplan_connectors.backend.tabular_sources import (
ADMIN_SCOPE,
READ_SCOPE,
WRITE_SCOPE,
SqlTabularSourceProvider,
)
from govoplan_connectors.backend.datasource_origins import (
ConnectorDatasourceOriginProvider,
)
from govoplan_connectors.backend.feeds import (
FEED_PRIVATE_PUBLISH_SCOPE,
FEED_PUBLISH_SCOPE,
ConnectorFeedProvider,
)
from govoplan_connectors.backend.provider_state import (
SANCTIONS_PROVIDER_ID,
TABULAR_PROVIDER_ID,
knowledge_provider_states,
sanctions_provider_states,
service_desk_provider_states,
tabular_provider_states,
)
from govoplan_connectors.backend.german_documentation import (
localize_documentation_topics,
)
MODULE_ID = "connectors"
MODULE_VERSION = "0.1.25"
TABULAR_SOURCE_INTERFACE_VERSION = "0.1.0"
DATASOURCE_ORIGIN_INTERFACE_VERSION = "0.1.0"
SANCTIONS_SNAPSHOT_INTERFACE_VERSION = "1.0.0"
FEED_INTERFACE_VERSION = "0.2.0"
CONNECTOR_RUNTIME_INTERFACE_VERSION = "1.0.0"
ARCHITECTURE = ModuleArchitectureDeclaration(
layer="data_reporting_integration",
kind="integration",
maturity="vertical_slice",
evidence=(
ModuleMaturityEvidence(
kind="test",
reference="tests/test_tabular_sources.py",
summary="Exercises tenant-safe immutable tabular snapshots and bounded reads.",
),
ModuleMaturityEvidence(
kind="test",
reference="tests/test_tabular_origin_provider.py",
summary="Exercises exact managed-file versions, reviewed refresh, live SQL projection, configuration drift, and tenant isolation.",
),
ModuleMaturityEvidence(
kind="test",
reference="tests/test_tabular_adapters.py",
summary="Exercises bounded CSV/XLSX parsing and the credential-governed read-only PostgreSQL adapter.",
),
ModuleMaturityEvidence(
kind="test",
reference="tests/test_sanctions_sources.py",
summary="Exercises source acquisition health, checksums, retries, and immutable evidence.",
),
ModuleMaturityEvidence(
kind="recovery",
reference="tests/test_recovery.py",
summary="Proves atomic snapshot commits, idempotent replay, distributed fences, tamper rejection, and unknown external-effect handling.",
),
ModuleMaturityEvidence(
kind="test",
reference="tests/test_governed_runtime.py",
summary="Exercises immutable definition revisions, protected local overrides, idempotent simulations, and explicit ambiguity review.",
),
ModuleMaturityEvidence(
kind="test",
reference="tests/test_mediawiki_connector.py",
summary="Exercises deterministic MediaWiki/BlueSpice discovery, stable mapping, bounded deltas, ACL-safe Search, migration loss diagnostics, and publication recovery states.",
),
ModuleMaturityEvidence(
kind="test",
reference="tests/test_service_desk_connector.py",
summary="Exercises Znuny/OTRS profile policy, stable ticket mapping, bounded synchronization, Search authorization, governed updates, and unknown-outcome evidence.",
),
ModuleMaturityEvidence(
kind="documentation",
reference="docs/CONNECTOR_SOURCE_LIFECYCLE.md",
summary="Defines source lifecycle, authority, evidence, and outage boundaries.",
),
),
known_limits=(
"Tabular origins support immutable snapshots, exact managed CSV/XLSX versions, and read-only PostgreSQL tables; arbitrary REST and other database adapters remain future providers.",
"Feed publication renders a governed document but does not yet push it to an external publishing endpoint.",
"MediaWiki/BlueSpice publication and governed-sync Znuny/OTRS ticket updates are explicit revision-checked write paths; generic simulations and other providers do not imply a live write capability.",
"Migration into native Wiki is preview-only; a target-side write worker and Wiki-owned mutation contract remain future work.",
"The Znuny/OTRS GenericInterface route map is deployment-defined; queues must be partitioned below the 10000-ticket identity bound and attachment bytes remain provider-side.",
),
supported_authority_modes=(
"external_authoritative",
"external_mirror",
"governed_sync",
"linked_reference",
),
owned_concepts=(
"external transport profiles",
"protocol interaction",
"immutable connector snapshots",
"connector acquisition health",
"external knowledge synchronization evidence",
"external service-desk transport and synchronization evidence",
),
non_owned_concepts=(
"datasource catalogue identity and lifecycle",
"domain records and business semantics",
"data transformations",
"screening dispositions",
"native Wiki spaces, pages, and revision semantics",
"ticket, article, customer, case, and helpdesk business semantics",
),
target_tested_providers=(
TABULAR_PROVIDER_ID,
SANCTIONS_PROVIDER_ID,
KNOWLEDGE_PROVIDER_ID,
SERVICE_DESK_PROVIDER_ID,
),
documentation=ModuleArchitectureDocumentation(
migration=("src/govoplan_connectors/backend/migrations/versions",),
upgrade=(
"docs/CONNECTOR_SOURCE_LIFECYCLE.md",
"docs/MEDIAWIKI_BLUESPICE_CONNECTOR.md",
"docs/ZNUNY_OTRS_CONNECTOR.md",
),
recovery=(
"docs/CONNECTOR_SOURCE_LIFECYCLE.md",
"docs/MEDIAWIKI_BLUESPICE_CONNECTOR.md",
"docs/ZNUNY_OTRS_CONNECTOR.md",
),
security=(
"docs/CONNECTOR_SOURCE_LIFECYCLE.md",
"docs/MEDIAWIKI_BLUESPICE_CONNECTOR.md",
"docs/ZNUNY_OTRS_CONNECTOR.md",
),
operations=(
"docs/CONNECTOR_SOURCE_LIFECYCLE.md",
"docs/MEDIAWIKI_BLUESPICE_CONNECTOR.md",
"docs/ZNUNY_OTRS_CONNECTOR.md",
),
),
)
EXTERNAL_PROVIDERS = (
ExternalProviderDeclaration(
id=TABULAR_PROVIDER_ID,
module_id=MODULE_ID,
label="Governed tabular source providers",
maturity="read",
operations=("discover", "search", "read", "preview", "dry_run"),
objects=(
ProviderObjectDeclaration(
object_type="tabular_source_snapshot",
field_groups=("identity", "schema", "rows", "source_provenance"),
authority_modes=("external_authoritative", "external_mirror"),
default_authority_mode="external_mirror",
),
),
behavior=ProviderBehaviorDeclaration(
revision_tokens="Source fingerprints, exact file versions, SQL configuration revisions, credential revisions, and discovery revisions are retained.",
concurrency="Reads may require the expected fingerprint; file versions remain pinned and SQL configuration or schema drift blocks reads until reviewed refresh.",
freshness="Snapshot acquisition time, exact/current file versions, and live SQL discovery health are exposed.",
health="Import validation, Files access/integrity, SQL authentication, configuration drift, schema drift, timeouts, and source unavailability are explicit without exposing secrets.",
max_read_items=1000,
idempotency="Feed imports accept a caller request key and replay the same committed immutable source without refetching.",
retry="Read-only acquisition may be retried only as a new deliberate request after a failed atomic operation.",
outcome_unknown="Provider reads do not mutate remote state; an uncertain database commit is resolved by the atomic recovery transaction.",
outcome_unknown_supported=False,
evidence="Rows, schema, fingerprint, source metadata, and acquisition provenance remain linked.",
correction="Import a replacement snapshot; retain the prior snapshot as evidence.",
rollback="Snapshot rows and the terminal recovery checkpoint commit or roll back together.",
reconciliation="Compare source and snapshot fingerprints before selecting a new current state.",
outage="Existing snapshots remain available and visibly stale; no live-source claim is made.",
classifications=("internal", "confidential", "restricted"),
purposes=("governed import", "dataflow input", "evidence reconstruction"),
retention="Datasources or the consuming domain supplies retention and hold policy.",
secret_handling="Snapshots and managed-file sources contain no connector credential; PostgreSQL uses a scoped Core credential-envelope reference and never stores or returns the resolved secret.",
),
capability_names=(
CAPABILITY_CONNECTORS_TABULAR_SOURCES,
CAPABILITY_DATASOURCE_ORIGINS,
),
interface_names=(
"connectors.tabular_sources",
"connectors.datasource_origins",
),
documentation_topic_ids=(
"connectors.authority-and-effects",
"connectors.tabular-sources",
),
),
ExternalProviderDeclaration(
id=SANCTIONS_PROVIDER_ID,
module_id=MODULE_ID,
label="Sanctions source snapshot provider",
maturity="read",
operations=("discover", "search", "read", "preview"),
objects=(
ProviderObjectDeclaration(
object_type="sanctions_source_snapshot",
field_groups=(
"source_identity",
"raw_evidence",
"entries",
"acquisition_health",
),
authority_modes=("external_authoritative", "external_mirror"),
default_authority_mode="external_mirror",
),
),
behavior=ProviderBehaviorDeclaration(
revision_tokens="Provider source version, ETag, Last-Modified, and SHA-256 digest are retained when available.",
concurrency="Refreshes use conditional source requests, a distributed per-tenant/provider fence, and immutable snapshots.",
freshness="Latest successful acquisition, source timestamp, and stale health are reported.",
health="Transport, parsing, source-change, and malformed-source states are explicit.",
max_read_items=5000,
idempotency="A caller request key identifies one acquisition run and replays its committed result without contacting the source again.",
retry="Bounded HTTP retries are safe because acquisition is read-only; failed runs require a new deliberate request key.",
timeout_seconds=30,
outcome_unknown="The external operation is read-only; snapshot rows and recovery evidence commit atomically.",
outcome_unknown_supported=False,
evidence="Raw source bytes, checksum, acquisition run, parser result, and normalized entry count are linked.",
correction="A corrected source creates a new immutable snapshot and acquisition run.",
rollback="A failed database transaction leaves no snapshot and the stale atomic fence resolves as failed.",
reconciliation="Compare source version and digest, then preserve both prior and corrected evidence.",
outage="The latest accepted snapshot stays usable with stale/unavailable source health.",
classifications=("public", "internal"),
purposes=("sanctions source acquisition", "compliance screening evidence"),
retention="Risk and Records policies determine accepted snapshot retention and legal holds.",
secret_handling="Public sources require no subject data or source credential; configured proxy secrets remain external to snapshots.",
),
capability_names=(CAPABILITY_CONNECTORS_SANCTIONS_SNAPSHOTS,),
interface_names=("connectors.sanctions_snapshots",),
documentation_topic_ids=(
"connectors.authority-and-effects",
"connectors.sanctions-snapshots",
),
),
ExternalProviderDeclaration(
id=KNOWLEDGE_PROVIDER_ID,
module_id=MODULE_ID,
label="MediaWiki and BlueSpice knowledge provider",
maturity="migrate",
operations=(
"discover",
"link",
"search",
"read",
"publish",
"synchronize",
"migrate",
"preview",
"dry_run",
),
objects=(
ProviderObjectDeclaration(
object_type="external_knowledge_page",
field_groups=(
"stable_identity",
"revisions",
"namespaces",
"categories",
"links",
"files",
"discussions",
"permissions",
"source_provenance",
),
authority_modes=(
"external_authoritative",
"external_mirror",
"governed_sync",
"linked_reference",
),
default_authority_mode="external_mirror",
),
),
behavior=ProviderBehaviorDeclaration(
revision_tokens="Stable MediaWiki page ids, revision ids, discovery revisions, recent-change cursors, content digests, and provider timestamps are retained.",
concurrency="Profile changes use optimistic revisions; publication supplies an expected provider revision and a durable idempotency fence.",
freshness="The latest discovery time, sync high-watermark, recent-change cursor, source update time, and health state are exposed.",
health="Product discovery, authentication, transport, mapping loss, ACL degradation, Search deferral, and outcome-unknown publication are explicit without exposing credentials.",
max_read_items=500,
idempotency="Backfill, delta synchronization, migration previews, and publication require caller keys; exact replays return the committed evidence and mismatched reuse is rejected.",
retry="Read-only discovery and synchronization may be retried with a new key; a publication with an unknown outcome must be reconciled before retry.",
timeout_seconds=30,
conflicts="Namespace and path mappings are explicit; migration previews block target-path and attachment conflicts, while publication rejects stale expected revisions.",
outcome_unknown="A timed-out publication remains outcome-unknown behind a durable recovery fence until the institutional operator verifies the provider revision.",
outcome_unknown_supported=True,
evidence="Mapped snapshots retain stable external references, revision identity, source hashes, transport provenance, ACLs, and bounded loss diagnostics.",
audit_event_types=(
"connectors.knowledge.profile.created",
"connectors.knowledge.profile.updated",
"connectors.knowledge.profile.discovered",
"connectors.knowledge.profile.synchronized",
"connectors.knowledge.migration.previewed",
"connectors.knowledge.page.published",
),
correction="A later provider revision or explicit publication creates a new connector snapshot revision while synchronization-run evidence remains retained.",
rollback="Local snapshot and terminal recovery evidence commit atomically; remote publication cannot be rolled back by a local transaction.",
reconciliation="Rediscover capabilities, compare stable page and revision ids, run a bounded delta or full backfill, and inspect unresolved publication evidence.",
outage="Existing snapshots remain visible only through current tenant and ACL authorization and are marked stale or unavailable; no provider freshness claim is made.",
classifications=("public", "internal", "confidential", "restricted"),
purposes=(
"external knowledge discovery",
"authorized federated search",
"knowledge synchronization",
"migration planning",
"governed publication",
),
retention="The tenant's connector, Records, and target Wiki policies determine snapshot and operation-evidence retention.",
secret_handling="Credentials are resolved from a scoped Core credential envelope, never placed in endpoint URLs, persisted snapshots, diagnostics, or API responses.",
),
capability_names=(KNOWLEDGE_CAPABILITY,),
interface_names=(KNOWLEDGE_CAPABILITY,),
documentation_topic_ids=(
"connectors.authority-and-effects",
"connectors.mediawiki-bluespice",
),
),
ExternalProviderDeclaration(
id=SERVICE_DESK_PROVIDER_ID,
module_id=MODULE_ID,
label="Znuny and OTRS-compatible service-desk provider",
maturity="synchronize",
operations=("discover", "link", "search", "read", "publish", "synchronize"),
objects=(
ProviderObjectDeclaration(
object_type="external_service_desk_ticket",
field_groups=(
"stable_identity",
"queue_and_routing",
"state_and_priority",
"users_and_organizations",
"articles",
"attachment_metadata",
"dynamic_fields",
"permissions",
"source_provenance",
),
authority_modes=(
"external_authoritative",
"external_mirror",
"governed_sync",
"linked_reference",
),
default_authority_mode="external_authoritative",
),
),
behavior=ProviderBehaviorDeclaration(
revision_tokens="Stable ticket, ticket-number, article, and attachment identities plus provider change timestamps, discovery revisions, cursors, and content hashes are retained.",
concurrency="Profiles use optimistic revisions; governed ticket updates require the synchronized provider revision and a durable idempotency fence.",
freshness="Discovery time, synchronization high-watermark, cursor, provider change time, observation time, and profile health remain explicit.",
health="Product/version verification, authentication, transport, route policy, mapping loss, ACL fallback, Search deferral, cursor bounds, and unknown update outcomes are explicit without exposing credentials.",
max_read_items=500,
idempotency="Every full, delta, or update operation requires a profile-wide caller key; exact replays return committed evidence and mismatched reuse is rejected.",
retry="Discovery and read-only synchronization can be retried deliberately; an update with an unknown outcome must be reconciled against the provider before retry.",
timeout_seconds=20,
conflicts="Queue inclusion, target refs, ACLs, dynamic fields, authority, and route mappings are explicit; updates reject stale provider revisions.",
outcome_unknown="A timed-out or inconclusive remote update remains outcome-unknown behind durable recovery evidence until an operator verifies the provider ticket revision.",
outcome_unknown_supported=True,
evidence="Mapped tickets retain stable external references, revisions, transport and mapping provenance, current ACLs, loss diagnostics, and operation effects; attachment bytes are never retained.",
audit_event_types=(
"connectors.service_desk.profile.created",
"connectors.service_desk.profile.updated",
"connectors.service_desk.profile.discovered",
"connectors.service_desk.profile.synchronized",
"connectors.service_desk.ticket.updated",
),
correction="A later provider revision updates or restores the connector projection while prior synchronization and mutation evidence stays retained.",
rollback="Local projection and terminal recovery evidence commit atomically; a remote provider update cannot be rolled back by a local transaction.",
reconciliation="Rediscover the deployment-defined GenericInterface routes, compare stable ticket revisions, finish or restart bounded synchronization, and inspect unresolved remote mutations.",
outage="Authorized existing projections remain visible with explicit stale health; no provider freshness or write-success claim is made during an outage.",
classifications=("internal", "confidential", "restricted"),
purposes=(
"external service-desk discovery",
"authorized federated search",
"ticket reference or import",
"bounded synchronization",
"governed ticket update",
),
retention="The tenant's connector, Tickets, Helpdesk, Cases, and Records policies determine projection and operation-evidence retention.",
secret_handling="Credentials resolve from a scoped Core envelope and use approved headers or POST bodies; they never enter GET URLs, snapshots, diagnostics, or API responses.",
),
capability_names=(SERVICE_DESK_CAPABILITY,),
interface_names=(SERVICE_DESK_CAPABILITY,),
documentation_topic_ids=(
"connectors.authority-and-effects",
"connectors.znuny-otrs",
),
),
)
def _permission(scope: str, label: str, description: str) -> PermissionDefinition:
module_id, resource, action = scope.split(":", 2)
return PermissionDefinition(
scope=scope,
label=label,
description=description,
category="Connectors",
level="tenant",
module_id=module_id,
resource=resource,
action=action,
)
PERMISSIONS = (
_permission(
READ_SCOPE,
"View tabular sources",
"Discover and preview policy-visible tabular connector sources.",
),
_permission(
WRITE_SCOPE,
"Manage tabular sources",
"Import and retire bounded tabular snapshots.",
),
_permission(
ADMIN_SCOPE,
"Administer connector sources",
"Manage tenant connector sources, versioned definitions, protected overrides, and review policies.",
),
_permission(
FEED_PUBLISH_SCOPE,
"Publish public feeds",
"Render provenance-bearing public RSS or Atom output from selected GovOPlaN objects.",
),
_permission(
FEED_PRIVATE_PUBLISH_SCOPE,
"Publish restricted feeds",
"Render tenant or private RSS or Atom output after the owning product surface has authorized every selected object.",
),
_permission(
SANCTIONS_READ_SCOPE,
"View sanctions source evidence",
"Inspect immutable sanctions snapshots and acquisition health.",
),
_permission(
SANCTIONS_REFRESH_SCOPE,
"Refresh sanctions sources",
"Acquire a new immutable sanctions source snapshot.",
),
_permission(
KNOWLEDGE_READ_SCOPE,
"View external knowledge",
"View authorized MediaWiki and BlueSpice profiles, pages, and synchronization evidence.",
),
_permission(
KNOWLEDGE_ADMIN_SCOPE,
"Administer external knowledge",
"Configure knowledge profiles, namespace mappings, authority, visibility, and discovery.",
),
_permission(
KNOWLEDGE_SYNC_SCOPE,
"Synchronize external knowledge",
"Run bounded MediaWiki and BlueSpice backfills and change synchronization.",
),
_permission(
KNOWLEDGE_PUBLISH_SCOPE,
"Publish external knowledge",
"Publish a governed page revision with concurrency and recovery evidence.",
),
_permission(
KNOWLEDGE_MIGRATE_SCOPE,
"Preview knowledge migration",
"Dry-run a bounded migration into Wiki and inspect loss or conflict diagnostics.",
),
_permission(
SERVICE_DESK_READ_SCOPE,
"View external service-desk tickets",
"View authorized Znuny/OTRS profiles, mapped tickets, and synchronization evidence.",
),
_permission(
SERVICE_DESK_ADMIN_SCOPE,
"Administer service-desk connectors",
"Configure GenericInterface routes, queues, fields, authority, visibility, and discovery.",
),
_permission(
SERVICE_DESK_SYNC_SCOPE,
"Synchronize service-desk tickets",
"Run bounded Znuny/OTRS backfills and change synchronization.",
),
_permission(
SERVICE_DESK_UPDATE_SCOPE,
"Update external service-desk tickets",
"Apply governed revision-checked ticket updates with durable recovery evidence.",
),
)
ROLE_TEMPLATES = (
RoleTemplate(
slug="connector_administrator",
name="Connector administrator",
description="Govern connector definitions, local configurations, simulations, and manual review.",
permissions=(
READ_SCOPE,
WRITE_SCOPE,
ADMIN_SCOPE,
FEED_PUBLISH_SCOPE,
FEED_PRIVATE_PUBLISH_SCOPE,
SANCTIONS_READ_SCOPE,
SANCTIONS_REFRESH_SCOPE,
KNOWLEDGE_READ_SCOPE,
KNOWLEDGE_ADMIN_SCOPE,
KNOWLEDGE_SYNC_SCOPE,
KNOWLEDGE_PUBLISH_SCOPE,
KNOWLEDGE_MIGRATE_SCOPE,
SERVICE_DESK_READ_SCOPE,
SERVICE_DESK_ADMIN_SCOPE,
SERVICE_DESK_SYNC_SCOPE,
SERVICE_DESK_UPDATE_SCOPE,
),
),
RoleTemplate(
slug="connector_source_manager",
name="Connector source manager",
description="Discover, import, preview, and retire tabular sources.",
permissions=(
READ_SCOPE,
WRITE_SCOPE,
FEED_PUBLISH_SCOPE,
SANCTIONS_READ_SCOPE,
SANCTIONS_REFRESH_SCOPE,
KNOWLEDGE_READ_SCOPE,
KNOWLEDGE_SYNC_SCOPE,
KNOWLEDGE_MIGRATE_SCOPE,
SERVICE_DESK_READ_SCOPE,
SERVICE_DESK_SYNC_SCOPE,
),
),
RoleTemplate(
slug="connector_source_reader",
name="Connector source reader",
description="Discover and preview tabular connector sources.",
permissions=(
READ_SCOPE,
SANCTIONS_READ_SCOPE,
KNOWLEDGE_READ_SCOPE,
SERVICE_DESK_READ_SCOPE,
),
),
)
def _router(_context):
from govoplan_connectors.backend.router import router
return router
def _provider(_context) -> SqlTabularSourceProvider:
return SqlTabularSourceProvider(registry=getattr(_context, "registry", None))
def _datasource_origin_provider(_context) -> ConnectorDatasourceOriginProvider:
return ConnectorDatasourceOriginProvider(
SqlTabularSourceProvider(registry=getattr(_context, "registry", None))
)
def _sanctions_snapshot_provider(
_context,
) -> SqlSanctionsSnapshotProvider:
return SqlSanctionsSnapshotProvider()
def _feed_provider(_context) -> ConnectorFeedProvider:
return ConnectorFeedProvider()
def _dsar_provider(_context) -> ConnectorsDsarProvider:
return ConnectorsDsarProvider()
def _knowledge_provider(_context) -> ExternalKnowledgeCapability:
return ExternalKnowledgeCapability()
def _service_desk_provider(_context) -> ExternalServiceDeskCapability:
return ExternalServiceDeskCapability()
def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
return {
"connector_definitions": (
session.query(ConnectorDefinition)
.filter(ConnectorDefinition.tenant_id == tenant_id)
.count()
),
"connector_configurations": (
session.query(ConnectorConfiguration)
.filter(ConnectorConfiguration.tenant_id == tenant_id)
.count()
),
"connector_simulation_runs": (
session.query(ConnectorSimulationRun)
.filter(ConnectorSimulationRun.tenant_id == tenant_id)
.count()
),
"connector_tabular_sources": (
session.query(ConnectorTabularSource)
.filter(
ConnectorTabularSource.tenant_id == tenant_id,
ConnectorTabularSource.deleted_at.is_(None),
)
.count()
),
"connector_sanctions_snapshots": (
session.query(ConnectorSanctionsSnapshot)
.filter(ConnectorSanctionsSnapshot.tenant_id == tenant_id)
.count()
),
"connector_sanctions_runs": (
session.query(ConnectorSanctionsAcquisitionRun)
.filter(ConnectorSanctionsAcquisitionRun.tenant_id == tenant_id)
.count()
),
"connector_knowledge_profiles": (
session.query(ConnectorKnowledgeProfile)
.filter(ConnectorKnowledgeProfile.tenant_id == tenant_id)
.count()
),
"connector_knowledge_objects": (
session.query(ConnectorKnowledgeObject)
.filter(
ConnectorKnowledgeObject.tenant_id == tenant_id,
ConnectorKnowledgeObject.status != "deleted",
)
.count()
),
"connector_knowledge_runs": (
session.query(ConnectorKnowledgeSyncRun)
.filter(ConnectorKnowledgeSyncRun.tenant_id == tenant_id)
.count()
),
"connector_service_desk_profiles": (
session.query(ConnectorServiceDeskProfile)
.filter(ConnectorServiceDeskProfile.tenant_id == tenant_id)
.count()
),
"connector_service_desk_objects": (
session.query(ConnectorServiceDeskObject)
.filter(
ConnectorServiceDeskObject.tenant_id == tenant_id,
ConnectorServiceDeskObject.status != "deleted",
)
.count()
),
"connector_service_desk_runs": (
session.query(ConnectorServiceDeskSyncRun)
.filter(ConnectorServiceDeskSyncRun.tenant_id == tenant_id)
.count()
),
}
manifest = ModuleManifest(
id=MODULE_ID,
name="Connectors",
version=MODULE_VERSION,
optional_dependencies=(
"access",
"audit",
"files",
"policy",
"datasources",
"portal",
"reporting",
"risk_compliance",
"search",
"tickets",
"helpdesk",
"cases",
"wiki",
),
required_capabilities=(
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
),
provides_interfaces=(
ModuleInterfaceProvider(
name="connectors.tabular_sources",
version=TABULAR_SOURCE_INTERFACE_VERSION,
),
ModuleInterfaceProvider(
name="connectors.tabular_snapshot_writer",
version=TABULAR_SOURCE_INTERFACE_VERSION,
),
ModuleInterfaceProvider(
name="connectors.datasource_origins",
version=DATASOURCE_ORIGIN_INTERFACE_VERSION,
),
ModuleInterfaceProvider(
name="connectors.sanctions_snapshots",
version=SANCTIONS_SNAPSHOT_INTERFACE_VERSION,
),
ModuleInterfaceProvider(
name="connectors.feeds",
version=FEED_INTERFACE_VERSION,
),
ModuleInterfaceProvider(
name="connectors.runtime_contract",
version=CONNECTOR_RUNTIME_INTERFACE_VERSION,
),
ModuleInterfaceProvider(
name=KNOWLEDGE_CAPABILITY,
version=KNOWLEDGE_INTERFACE_VERSION,
),
ModuleInterfaceProvider(
name=SERVICE_DESK_CAPABILITY,
version=SERVICE_DESK_INTERFACE_VERSION,
),
ModuleInterfaceProvider(name=CONNECTORS_DSAR_CAPABILITY, version="0.1.0"),
),
requires_interfaces=(
ModuleInterfaceRequirement(
name=CAPABILITY_FILES_TABULAR_CONTENT,
version_min="1.0.0",
version_max_exclusive="2.0.0",
optional=True,
),
),
permissions=PERMISSIONS,
role_templates=ROLE_TEMPLATES,
route_factory=_router,
frontend=FrontendModule(
module_id=MODULE_ID,
package_name="@govoplan/connectors-webui",
view_surfaces=(
ViewSurface(
id="connectors.admin.governed-configurations",
module_id=MODULE_ID,
kind="section",
label="Connector governance",
order=45,
),
ViewSurface(
id="connectors.admin.simulation-review",
module_id=MODULE_ID,
kind="section",
label="Connector simulation review",
parent_id="connectors.admin.governed-configurations",
order=20,
),
ViewSurface(
id="connectors.admin.external-knowledge",
module_id=MODULE_ID,
kind="section",
label="External knowledge",
parent_id="connectors.admin.governed-configurations",
order=30,
),
ViewSurface(
id="connectors.admin.external-service-desk",
module_id=MODULE_ID,
kind="section",
label="External service desk",
parent_id="connectors.admin.governed-configurations",
order=40,
),
),
),
capability_factories={
CAPABILITY_CONNECTORS_TABULAR_SOURCES: _provider,
CAPABILITY_CONNECTORS_TABULAR_SNAPSHOT_WRITER: _provider,
CAPABILITY_DATASOURCE_ORIGINS: _datasource_origin_provider,
CAPABILITY_CONNECTORS_SANCTIONS_SNAPSHOTS: (_sanctions_snapshot_provider),
CAPABILITY_CONNECTORS_FEEDS: _feed_provider,
KNOWLEDGE_CAPABILITY: _knowledge_provider,
SERVICE_DESK_CAPABILITY: _service_desk_provider,
CONNECTORS_DSAR_CAPABILITY: _dsar_provider,
},
capability_documentation={
CONNECTORS_DSAR_CAPABILITY: CapabilityDocumentation(
label="Connector data-subject request provider",
summary=(
"Exports minimized operator attribution without connector secrets, "
"external payloads, or transport evidence."
),
contract_version="0.1.0",
),
},
tenant_summary_providers=(_tenant_summary,),
search_sources=(
SearchSourceProviderRegistration(
id=KNOWLEDGE_PROVIDER_ID,
factory=create_external_knowledge_search_source,
order=65,
),
SearchSourceProviderRegistration(
id=SERVICE_DESK_PROVIDER_ID,
factory=create_external_service_desk_search_source,
order=66,
),
),
architecture=ARCHITECTURE,
external_providers=EXTERNAL_PROVIDERS,
external_provider_state_providers=(
ExternalProviderStateProviderRegistration(
module_id=MODULE_ID,
provider_id=TABULAR_PROVIDER_ID,
provider=tabular_provider_states,
),
ExternalProviderStateProviderRegistration(
module_id=MODULE_ID,
provider_id=SANCTIONS_PROVIDER_ID,
provider=sanctions_provider_states,
),
ExternalProviderStateProviderRegistration(
module_id=MODULE_ID,
provider_id=KNOWLEDGE_PROVIDER_ID,
provider=knowledge_provider_states,
),
ExternalProviderStateProviderRegistration(
module_id=MODULE_ID,
provider_id=SERVICE_DESK_PROVIDER_ID,
provider=service_desk_provider_states,
),
),
migration_spec=MigrationSpec(
module_id=MODULE_ID,
metadata=Base.metadata,
script_location=str(Path(__file__).with_name("migrations") / "versions"),
retirement_supported=True,
retirement_provider=drop_table_retirement_provider(
ConnectorServiceDeskSyncRun,
ConnectorServiceDeskObject,
ConnectorServiceDeskProfile,
ConnectorKnowledgeSyncRun,
ConnectorKnowledgeObject,
ConnectorKnowledgeProfile,
ConnectorSimulationRun,
ConnectorConfiguration,
ConnectorDefinitionRevision,
ConnectorDefinition,
ConnectorSanctionsSnapshot,
ConnectorSanctionsAcquisitionRun,
ConnectorTabularSource,
label="Connectors",
),
retirement_notes=(
"Destructive retirement drops connector-owned source snapshots after "
"the installer captures a database snapshot."
),
),
uninstall_guard_providers=(
persistent_table_uninstall_guard(
ConnectorServiceDeskSyncRun,
ConnectorServiceDeskObject,
ConnectorServiceDeskProfile,
ConnectorKnowledgeSyncRun,
ConnectorKnowledgeObject,
ConnectorKnowledgeProfile,
ConnectorSimulationRun,
ConnectorConfiguration,
ConnectorDefinitionRevision,
ConnectorDefinition,
ConnectorSanctionsSnapshot,
ConnectorSanctionsAcquisitionRun,
ConnectorTabularSource,
label="Connectors",
),
),
documentation=localize_documentation_topics((
DocumentationTopic(
id="connectors.data-subject-requests",
title="Connector data-subject requests",
summary=(
"Export accountable connector activity without disclosing credentials "
"or external data."
),
body=(
"Connectors correlates only an exact tenant account identifier and can "
"narrow an already verified search to one source, acquisition, "
"definition, configuration, simulation, external-knowledge or service-desk "
"profile, or connector operation. The export identifies the subject's "
"configuration, acquisition, simulation, external-operation, and review activity "
"using bounded lifecycle metadata. It never includes credential or "
"endpoint references, source rows, external responses, request payloads, "
"mapping and configuration documents, diagnostics, provenance, hashes, "
"or transport evidence. Connector attribution remains immutable "
"governance and external-operation evidence and is retained rather than "
"automatically erased. Email or object identifiers without a verified "
"account identifier do not establish a match."
),
layer="configured",
documentation_types=("admin", "user"),
audience=("user", "operator", "module_admin", "auditor"),
related_modules=("core", "access", "audit", "policy"),
order=37,
metadata={
"help_contexts": ["privacy.data-subject-requests"],
"consequence_classes": {
"export_operator_attribution": (
"Returns minimized connector activity for the exact account."
),
"exclude_connector_secrets": (
"Never returns credentials, endpoints, external rows, or evidence payloads."
),
"retain_connector_evidence": (
"Preserves configuration and external-operation accountability."
),
},
},
),
DocumentationTopic(
id="connectors.governed-configuration",
title="Govern connector definitions and simulations",
summary="Version connector schemas and mappings while preserving tenant-local overrides and review evidence.",
body=(
"Connector administrators create package-managed or local definitions that explicitly declare provider, protocol, capabilities, schemas, mapping rules, validation, preview support, audit expectations, privacy, retention, limits, and retry metadata. Every definition change creates an immutable revision. A tenant configuration pins one revision and stores only a credential reference; package updates remain available but do not change the effective configuration until an administrator adopts them. Local override leaf paths are displayed as protected and are reapplied when an update is adopted. Dry-runs and simulations are bounded, redact configured fields, are idempotent by caller key, and retain configuration, mapping, input, and external revision provenance. Ambiguous results follow the configuration policy: manual review, quarantine, or rejection. Pending and quarantined evidence requires an explicit approve or reject decision with a reason. Provider-specific live writes are not implied by a successful generic simulation."
),
layer="configured",
documentation_types=("admin", "user"),
audience=("operator", "module_admin", "integration_admin"),
related_modules=("policy", "audit", "dataflow", "ops"),
order=38,
metadata={
"kind": "workflow",
"help_contexts": ["connectors.admin.governed-configurations"],
"prerequisites": [
"A connector definition has been installed or authored.",
"Credential material is stored outside the connector URL and referenced by an approved secret identifier.",
],
"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",
title="Connector authority and effect behavior",
summary="Connector direction, technical maturity, and configured source authority are separate and must remain visible.",
body=(
"A connector can consume, publish, or work bidirectionally and can mature from discovery through replacement. "
"Each binding separately states whether GovOPlaN is authoritative, follows an external authority, keeps a mirror, synchronizes under conflict rules, adds a governance overlay, or retains only a link. "
"Writable providers must explain revisions, limits, idempotency, outcome-unknown handling, evidence, reconciliation, correction, outage behavior, and secret requirements."
),
layer="available",
documentation_types=("admin", "user"),
audience=("operator", "module_admin", "power_user", "product_owner"),
related_modules=("datasources", "dataflow", "ops", "policy", "audit"),
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(
id="connectors.runtime-preview-contract",
title="Connector previews and diagnostics",
summary="Use one bounded, redacted dry-run shape across external transports.",
body=(
"Connectors owns endpoint discovery, authentication hand-off, transport limits, retries, and protocol health. "
"Domain modules own field mapping, validation, reconciliation, and record mutation. The shared Core runtime "
"contract reports redacted effects and diagnostics with source revisions, fingerprints, and immutable input hashes. "
"Tabular previews enforce effective row, serialized-byte, and elapsed-time ceilings and report limit truncation "
"as structured diagnostics. A commit must reject stale, truncated, conflicting, or error-bearing previews, and "
"credentials never appear in URLs or samples."
),
layer="available",
documentation_types=("admin", "user"),
audience=("operator", "module_admin", "power_user"),
related_modules=("addresses", "datasources", "dataflow", "policy", "audit"),
order=40,
),
DocumentationTopic(
id="connectors.tabular-sources",
title="Governed tabular sources",
summary="Provider-neutral source discovery and bounded reads for Dataflow.",
body=(
"Connectors owns source configuration, access checks, schema discovery, "
"fingerprints, and bounded reads. Dataflow stores only opaque source "
"references and expected fingerprints. Each source declares its live, "
"cached, file-backed, or static mode, structured health, and supported "
"projection, filter, aggregation, sorting, and pagination pushdown. "
"Immutable JSON/CSV snapshots remain available. Managed CSV/XLSX "
"sources use the optional Files capability, pin an exact authorized "
"version, apply archive and expansion limits, and require explicit "
"refresh before adopting a newer version. The PostgreSQL adapter uses "
"an active governed configuration and scoped Core credential envelope, "
"reflects simple schema/table identifiers, runs read-only bounded "
"projection and pagination, and blocks configuration, credential, or "
"schema drift until reviewed refresh. Credentials, endpoints, storage "
"keys, and raw file internals are never exposed through the origin."
),
layer="available",
documentation_types=("admin", "user"),
audience=("operator", "module_admin", "power_user"),
related_modules=("dataflow", "files", "reporting", "risk_compliance"),
order=40,
),
DocumentationTopic(
id="connectors.rss-atom",
title="RSS and Atom feeds",
summary="Import governed feed snapshots and emit visibility-filtered feeds.",
body=(
"Connectors owns bounded, SSRF-protected RSS/Atom transport and XML "
"parsing. Imported entries become immutable tabular snapshots exposed "
"through Datasources, including acquisition, freshness, ETag, content "
"digest, and source provenance. Emission accepts only provenance-bearing "
"event, publication, case, or report selections from an owning surface. "
"The requested audience determines the visibility ceiling: public output "
"contains only public entries, while tenant or private output requires a "
"separate restricted-feed permission. Callers cannot supply their own "
"visibility allow-list. Portal or Reporting owns durable publication "
"routes and must re-authorize access on every restricted feed request. "
"A separate RSS module is only warranted if GovOPlaN later needs a "
"dedicated feed-reader product surface."
),
layer="available",
documentation_types=("admin", "user"),
audience=("operator", "module_admin", "power_user"),
related_modules=("datasources", "dataflow", "portal", "reporting"),
order=42,
),
DocumentationTopic(
id="connectors.mediawiki-bluespice",
title="Connect MediaWiki and BlueSpice knowledge",
summary="Discover, synchronize, search, publish, and preview migration of external knowledge without weakening source permissions.",
body=(
"A connector administrator first creates a governed MediaWiki Action API configuration whose endpoint passes Core egress and peer validation; credentials remain in a scoped credential envelope. A knowledge profile then maps each included source namespace to a target Wiki space and path prefix, selects source authority, and sets a tenant or restricted fallback ACL. Discovery records product, version, extensions, namespaces, authentication state, capabilities, and loss diagnostics. Run a bounded full backfill once, then cursor-based deltas for revisions, moves, deletions, and permission changes. Stable page and revision identifiers, canonical links, redirects, files, categories, links, discussions, provenance, and current ACLs remain attached to the connector snapshot. Search indexes only active, non-deleted pages and rechecks the current profile, tenant, permission, and ACL before returning every result; disabling Connectors removes its Search projection. Publication requires an expected external revision, an idempotency key, and durable outcome evidence. An unknown provider outcome must be reconciled before retry. Migration into native Wiki is deliberately preview-only in this slice: the dry-run reports target-path collisions, attachment-name conflicts, unsupported macros, truncation, and source fingerprints; a successful preview does not write native Wiki pages. Existing authorized snapshots can remain visible during a provider outage, but health and freshness stay explicit and no current-source claim is made."
),
layer="configured",
documentation_types=("admin", "user"),
audience=(
"operator",
"module_admin",
"integration_admin",
"knowledge_manager",
"auditor",
),
related_modules=("core", "search", "wiki", "files", "audit", "policy"),
order=43,
translations={
"de": {
"title": "Wissen aus MediaWiki und BlueSpice anbinden",
"summary": "Externes Wissen erkennen, synchronisieren, durchsuchen, veröffentlichen und eine Migration prüfen, ohne Quellberechtigungen abzuschwächen.",
"body": (
"Eine Connector-Administration erstellt zuerst eine gesteuerte Konfiguration für die MediaWiki Action API. Der Endpunkt unterliegt der zentralen Ausgangs- und Gegenstellenprüfung; Zugangsdaten bleiben in einem zweckgebundenen Zugangsdaten-Umschlag. Das Wissensprofil ordnet jeden eingeschlossenen Quellnamensraum einem Zielbereich und Pfadpräfix in Wiki zu und legt Quellhoheit sowie eine mandantenweite oder eingeschränkte Ersatz-ACL fest. Die Erkennung dokumentiert Produkt, Version, Erweiterungen, Namensräume, Authentifizierungsstatus, Fähigkeiten und Verlustdiagnosen. Nach einem begrenzten Vollabgleich folgen cursorbasierte Änderungen für Revisionen, Verschiebungen, Löschungen und Berechtigungen. Stabile Seiten- und Revisionskennungen, kanonische Links, Weiterleitungen, Dateien, Kategorien, Links, Diskussionen, Herkunft und aktuelle ACLs bleiben am Snapshot. Search indiziert nur aktive, nicht gelöschte Seiten und prüft bei jedem Treffer Profilstatus, Mandant, Berechtigung und ACL erneut; bei Deaktivierung von Connectors wird dessen Suchprojektion entfernt. Veröffentlichungen erfordern die erwartete externe Revision, einen Idempotenzschlüssel und dauerhafte Ergebnisevidenz. Ein unbekanntes Ergebnis muss vor einem erneuten Versuch abgeglichen werden. Die Migration in das native Wiki ist in diesem Ausbauschritt ausschließlich eine Vorschau: Sie meldet Pfad- und Anhangskonflikte, nicht unterstützte Makros, Begrenzungen und Quellfingerabdrücke, schreibt aber keine Wiki-Seiten. Bei einem Ausfall dürfen bestehende Snapshots nur für weiterhin Berechtigte sichtbar bleiben; Zustand und Aktualität bleiben ausdrücklich erkennbar."
),
}
},
metadata={
"kind": "guide",
"help_contexts": ["connectors.admin.external-knowledge"],
"prerequisites": [
"An active governed MediaWiki Action API configuration exists.",
"Namespace targets and fallback ACLs have been reviewed.",
"The Search and Wiki modules are optional and remain capability-separated.",
],
"outcome": "External knowledge remains identity-stable, loss-visible, ACL-safe, and migration-ready.",
"verification": "Rediscover the profile, run a keyed delta, inspect health and diagnostics, verify an allowed and denied Search principal, and run a migration dry-run before any target-side work.",
},
),
DocumentationTopic(
id="connectors.sanctions-snapshots",
title="Sanctions source snapshots",
summary=(
"Acquire immutable, checksum-verifiable sanctions list "
"evidence without transmitting screening subjects."
),
body=(
"Connectors provides a deterministic synthetic fixture and "
"the official United Nations Security Council consolidated "
"XML source. Each fetch records conditional transport "
"evidence, bounded retries, health state, source metadata, "
"raw evidence, and a SHA-256 checksum. Refreshes acquire a "
"distributed recovery fence before provider I/O; the immutable "
"snapshot and terminal recovery checkpoint then commit in one "
"transaction. A repeated request key returns the same result. "
"Risk Compliance owns "
"normalization, matching, legal review, and dispositions."
),
layer="available",
documentation_types=("admin", "user"),
audience=("operator", "module_admin", "compliance_reviewer"),
related_modules=("risk_compliance", "dataflow"),
order=41,
),
DocumentationTopic(
id="connectors.znuny-otrs",
title="Connect Znuny and OTRS-compatible service desks",
summary="Link, import, synchronize, search, and govern updates to external tickets without collapsing Tickets, Helpdesk, or Cases semantics.",
body=(
"A connector administrator first creates an active governed configuration for a Znuny or OTRS-compatible GenericInterface REST endpoint and keeps credentials in a scoped Core credential envelope. Because GenericInterface route paths and methods are defined by each provider deployment, the service-desk profile explicitly maps search, ticket-read, optional update, and browser-link routes. The profile chooses link, snapshot import, or ongoing synchronization; separately it records external, mirror, linked-reference, or governed-sync authority. Queue mappings decide inclusion, optional target queue references, and current tenant or restricted ACLs. Dynamic-field mappings declare included fields, governed names, and value types. Discovery verifies endpoint health, the exact configuration revision, product/version evidence, and safe technical maturity. A changed endpoint, governed configuration, or route map invalidates discovery and prior projections: synchronization and updates require rediscovery, while Search stays closed until a new full reconciliation. Integration or mapping changes also reset the cursor for a full reconciliation. A bounded full run reconciles stable ticket identities, then synchronize mode changes to cursor-based, revision-aware deltas with overlap-safe provider timestamps; delta cannot bootstrap an unreconciled profile, and supplied cursors must match committed state. The connector maps queues, state, priority, type, owners, responsible users, customers, organizations, services, SLAs, articles, attachment metadata, dynamic fields, provenance, and structured loss diagnostics. It never retains attachment bytes. GenericInterface has no portable standard ticket ACL, so provider-supplied GovOPlaN ACL metadata wins when present; otherwise reviewed queue or restricted profile defaults apply. Search includes only active authorized projections and rechecks tenant, profile status, read scope, current configuration discovery, and current ACL for every result. A ticket remains an external ticket reference: creating or relating a GovOPlaN Ticket, Helpdesk item, or Case belongs to those modules. Remote updates are available only in governed-sync mode after discovery confirms an update route, require the synchronized external revision and a unique idempotency key, and retain durable outcome evidence. If the result is unknown, operators must inspect the provider revision before retry. Providers with more than 10000 identities must be partitioned into queue-scoped profiles; too many tickets at one timestamp also require narrower partitions. During outages, existing authorized projections remain visibly stale and never imply current provider state."
),
layer="configured",
documentation_types=("admin", "user"),
audience=(
"operator",
"module_admin",
"integration_admin",
"service_desk_manager",
"auditor",
),
related_modules=(
"core",
"search",
"tickets",
"helpdesk",
"cases",
"audit",
"policy",
),
order=44,
translations={
"de": {
"title": "Znuny- und OTRS-kompatible Service-Desks anbinden",
"summary": "Externe Tickets verknüpfen, importieren, synchronisieren, durchsuchen und gesteuert aktualisieren, ohne die Fachsemantik von Tickets, Helpdesk oder Cases zu vermischen.",
"body": (
"Die Connector-Administration erstellt zuerst eine aktive, gesteuerte Konfiguration für einen Znuny- oder OTRS-kompatiblen GenericInterface-REST-Endpunkt; Zugangsdaten bleiben in einem zweckgebundenen Core-Umschlag. Da Pfade und Methoden im GenericInterface je Installation festgelegt werden, ordnet das Service-Desk-Profil Suche, Ticketabruf, optionale Aktualisierung und Browserlink ausdrücklich zu. Das Profil wählt Verknüpfung, Snapshot-Import oder fortlaufende Synchronisierung und legt getrennt davon Quellhoheit, Spiegelung, Referenz oder gesteuerte beidseitige Aktualisierung fest. Warteschlangen-Zuordnungen bestimmen Einschluss, optionale Zielreferenz und aktuelle mandantenweite oder eingeschränkte ACLs. Dynamische Felder erhalten freigegebene Namen und Datentypen. Die Erkennung prüft Gesundheit, die genaue Konfigurationsrevision, Produkt-/Versionsnachweis und technische Reife. Ein geänderter Endpunkt, eine geänderte gesteuerte Konfiguration oder Routenabbildung macht Nachweis und bisherige Projektionen ungültig; Synchronisierung und Aktualisierungen erfordern erneute Erkennung, Search zusätzlich einen neuen Vollabgleich. Änderungen an Integration oder Abbildungen setzen den Cursor ebenfalls zurück. Erst nach einem begrenzten Vollabgleich verwendet der Synchronisierungsmodus revisionsbewusste, überlappungssichere Delta-Cursor; Delta kann kein unabgeglichenes Profil initialisieren und übergebene Cursor müssen dem gespeicherten Stand entsprechen. Abgebildet werden stabile Ticket-, Artikel- und Anhangskennungen, Warteschlange, Status, Priorität, Typ, Bearbeitende, Kundschaft, Organisationen, Services, SLAs, Artikel, Anhangsmetadaten, dynamische Felder, Herkunft und Verlustdiagnosen. Anhangsdaten werden nie gespeichert. Da das Standard-GenericInterface keine portable Ticket-ACL liefert, haben ausdrücklich gelieferte GovOPlaN-ACL-Metadaten Vorrang; sonst greifen geprüfte Warteschlangen- oder eingeschränkte Profilvorgaben. Search prüft bei jedem Treffer Mandant, Profilstatus, Leserecht, aktuellen Konfigurationsnachweis und aktuelle ACL neu. Ein externes Ticket bleibt eine externe Referenz; fachliche Tickets, Helpdesk-Vorgänge und Cases werden ausschließlich von den jeweiligen Modulen erzeugt oder verknüpft. Externe Änderungen sind nur im Modus der gesteuerten Synchronisierung mit erkannter Update-Route, erwarteter Quellrevision und eindeutigem Idempotenzschlüssel zulässig. Ein unbekanntes Ergebnis muss vor einem erneuten Versuch am Anbieter geprüft werden. Profile mit mehr als 10000 Ticketkennungen oder zu vielen Änderungen am selben Zeitstempel müssen nach Warteschlangen enger aufgeteilt werden. Bei einem Ausfall bleiben bestehende Projektionen nur für weiterhin Berechtigte und mit sichtbarer veralteter Gesundheit verfügbar."
),
}
},
metadata={
"kind": "guide",
"help_contexts": ["connectors.admin.external-service-desk"],
"prerequisites": [
"An active governed Znuny/OTRS GenericInterface REST configuration exists.",
"Deployment-defined routes, queue partitions, authority, and fallback ACLs have been reviewed.",
"Tickets, Helpdesk, Cases, and Search remain optional capability-separated consumers.",
],
"outcome": "External tickets remain identity-stable, loss-visible, ACL-safe, recoverable, and semantically separate from GovOPlaN domain records.",
"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
)
def get_manifest() -> ModuleManifest:
return manifest
__all__ = [
"MODULE_ID",
"MODULE_VERSION",
"DATASOURCE_ORIGIN_INTERFACE_VERSION",
"SANCTIONS_SNAPSHOT_INTERFACE_VERSION",
"TABULAR_SOURCE_INTERFACE_VERSION",
"get_manifest",
"manifest",
]