feat(docs): validate owner structured translations
Module Package Release / publish-packages (push) Successful in 13s
Module Package Release / publish-packages (push) Successful in 13s
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "govoplan-core"
|
name = "govoplan-core"
|
||||||
version = "0.1.37"
|
version = "0.1.38"
|
||||||
description = "Reusable GovOPlaN platform core, access, tenancy, and RBAC components."
|
description = "Reusable GovOPlaN platform core, access, tenancy, and RBAC components."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable, Iterable, Mapping, Sequence
|
from collections.abc import Callable, Iterable, Mapping, Sequence
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field, replace
|
||||||
from typing import Any, Literal, Protocol, TYPE_CHECKING
|
from typing import Any, Literal, Protocol, TYPE_CHECKING
|
||||||
|
|
||||||
from govoplan_core.core.information_governance import ModuleInformationGovernance
|
from govoplan_core.core.information_governance import ModuleInformationGovernance
|
||||||
@@ -687,3 +687,53 @@ class ModuleManifest:
|
|||||||
# runtime module ID changes.
|
# runtime module ID changes.
|
||||||
permission_namespace: str | None = None
|
permission_namespace: str | None = None
|
||||||
workflow_definitions: tuple["WorkflowDefinitionContribution", ...] = ()
|
workflow_definitions: tuple["WorkflowDefinitionContribution", ...] = ()
|
||||||
|
|
||||||
|
|
||||||
|
def with_documentation_structured_translations(
|
||||||
|
manifest: ModuleManifest,
|
||||||
|
*,
|
||||||
|
locale: str,
|
||||||
|
translations: Mapping[str, Mapping[str, Any]],
|
||||||
|
) -> ModuleManifest:
|
||||||
|
"""Merge module-owned structured documentation translations by topic id.
|
||||||
|
|
||||||
|
The helper keeps feature prose in its owning module while giving every
|
||||||
|
manifest the same fail-closed merge behavior. Unknown topic ids and
|
||||||
|
incomplete or shape-changing locale maps are rejected immediately.
|
||||||
|
"""
|
||||||
|
|
||||||
|
locale = locale.strip()
|
||||||
|
if not locale:
|
||||||
|
raise ValueError("structured documentation locale must not be empty")
|
||||||
|
|
||||||
|
topics_by_id = {topic.id: topic for topic in manifest.documentation}
|
||||||
|
unknown_topic_ids = sorted(set(translations) - set(topics_by_id))
|
||||||
|
if unknown_topic_ids:
|
||||||
|
raise ValueError(
|
||||||
|
"structured documentation translations reference unknown topic ids: "
|
||||||
|
+ ", ".join(unknown_topic_ids)
|
||||||
|
)
|
||||||
|
|
||||||
|
localized_topics: list[DocumentationTopic] = []
|
||||||
|
for topic in manifest.documentation:
|
||||||
|
translation = translations.get(topic.id)
|
||||||
|
if translation is None:
|
||||||
|
localized_topics.append(topic)
|
||||||
|
continue
|
||||||
|
|
||||||
|
structured_translations = dict(topic.structured_translations)
|
||||||
|
structured_translations[locale] = translation
|
||||||
|
localized_topic = replace(
|
||||||
|
topic,
|
||||||
|
structured_translation_version=DOCUMENTATION_STRUCTURED_TRANSLATION_VERSION,
|
||||||
|
structured_translations=structured_translations,
|
||||||
|
)
|
||||||
|
issues = documentation_structured_translation_issues(localized_topic)
|
||||||
|
if issues:
|
||||||
|
raise ValueError(
|
||||||
|
f"invalid {locale!r} structured documentation translation for "
|
||||||
|
f"{topic.id!r}: {'; '.join(issues)}"
|
||||||
|
)
|
||||||
|
localized_topics.append(localized_topic)
|
||||||
|
|
||||||
|
return replace(manifest, documentation=tuple(localized_topics))
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from govoplan_core.core.modules import (
|
|||||||
ModuleManifest,
|
ModuleManifest,
|
||||||
localized_documentation_metadata,
|
localized_documentation_metadata,
|
||||||
user_workflow_scope_condition_issues,
|
user_workflow_scope_condition_issues,
|
||||||
|
with_documentation_structured_translations,
|
||||||
)
|
)
|
||||||
from govoplan_core.core.registry import PlatformRegistry, RegistryError
|
from govoplan_core.core.registry import PlatformRegistry, RegistryError
|
||||||
|
|
||||||
@@ -136,6 +137,45 @@ class DocumentationTopicContractTests(unittest.TestCase):
|
|||||||
with self.assertRaisesRegex(RegistryError, "preserve list length"):
|
with self.assertRaisesRegex(RegistryError, "preserve list length"):
|
||||||
registry_for(incomplete_shape).validate()
|
registry_for(incomplete_shape).validate()
|
||||||
|
|
||||||
|
def test_manifest_helper_merges_and_validates_owner_translations(self) -> None:
|
||||||
|
topic = DocumentationTopic(
|
||||||
|
id="example.workflow.localized",
|
||||||
|
title="Run task",
|
||||||
|
summary="Run the task.",
|
||||||
|
metadata={"steps": ["Review", "Execute"]},
|
||||||
|
)
|
||||||
|
manifest = ModuleManifest(
|
||||||
|
id="example",
|
||||||
|
name="Example",
|
||||||
|
version="1.0.0",
|
||||||
|
documentation=(topic,),
|
||||||
|
)
|
||||||
|
|
||||||
|
localized = with_documentation_structured_translations(
|
||||||
|
manifest,
|
||||||
|
locale="de",
|
||||||
|
translations={
|
||||||
|
topic.id: {"steps": ["Prüfen", "Ausführen"]},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
["Prüfen", "Ausführen"],
|
||||||
|
localized.documentation[0].structured_translations["de"]["steps"],
|
||||||
|
)
|
||||||
|
with self.assertRaisesRegex(ValueError, "unknown topic ids"):
|
||||||
|
with_documentation_structured_translations(
|
||||||
|
manifest,
|
||||||
|
locale="de",
|
||||||
|
translations={"missing.topic": {"steps": ["Prüfen", "Ausführen"]}},
|
||||||
|
)
|
||||||
|
with self.assertRaisesRegex(ValueError, "preserve list length"):
|
||||||
|
with_documentation_structured_translations(
|
||||||
|
manifest,
|
||||||
|
locale="de",
|
||||||
|
translations={topic.id: {"steps": ["Prüfen"]}},
|
||||||
|
)
|
||||||
|
|
||||||
def test_documentation_configuration_and_source_extensions_are_validated(self) -> None:
|
def test_documentation_configuration_and_source_extensions_are_validated(self) -> None:
|
||||||
resolver = lambda _context, keys: { # noqa: E731
|
resolver = lambda _context, keys: { # noqa: E731
|
||||||
key: DocumentationConfigurationDecision(key=key, state="enabled")
|
key: DocumentationConfigurationDecision(key=key, state="enabled")
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/core-webui",
|
"name": "@govoplan/core-webui",
|
||||||
"version": "0.1.37",
|
"version": "0.1.38",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@govoplan/core-webui",
|
"name": "@govoplan/core-webui",
|
||||||
"version": "0.1.37",
|
"version": "0.1.38",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@govoplan/access-webui": "file:../../govoplan-access/webui",
|
"@govoplan/access-webui": "file:../../govoplan-access/webui",
|
||||||
"@govoplan/addresses-webui": "file:../../govoplan-addresses/webui",
|
"@govoplan/addresses-webui": "file:../../govoplan-addresses/webui",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/core-webui",
|
"name": "@govoplan/core-webui",
|
||||||
"version": "0.1.37",
|
"version": "0.1.38",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@govoplan/core-webui",
|
"name": "@govoplan/core-webui",
|
||||||
"version": "0.1.37",
|
"version": "0.1.38",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.20",
|
"@govoplan/access-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git#v0.1.20",
|
||||||
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.19",
|
"@govoplan/admin-webui": "git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git#v0.1.19",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/core-webui",
|
"name": "@govoplan/core-webui",
|
||||||
"version": "0.1.37",
|
"version": "0.1.38",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@govoplan/core-webui",
|
"name": "@govoplan/core-webui",
|
||||||
"version": "0.1.37",
|
"version": "0.1.38",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user