feat(connectors): govern RSS and Atom publication
Module Package Release / publish-packages (push) Successful in 11s

This commit is contained in:
2026-08-21 17:36:19 +02:00
parent de8e74b866
commit e6ab8291ec
9 changed files with 182 additions and 18 deletions
+42 -4
View File
@@ -22,6 +22,7 @@ from govoplan_core.core.feeds import (
FeedCapabilityError,
FeedEntry,
FeedRenderRequest,
FeedVisibility,
)
from govoplan_core.core.sanctions import SanctionsSnapshotReference
from govoplan_core.db.session import get_session
@@ -29,6 +30,7 @@ from govoplan_connectors.backend.schemas import (
FeedAcquireRequest,
FeedDocumentResponse,
FeedImportRequest,
FeedPublicationEntryPayload,
FeedRenderPayload,
SanctionsAcquisitionRunListResponse,
SanctionsAcquisitionRunResponse,
@@ -47,7 +49,12 @@ from govoplan_connectors.backend.schemas import (
TabularSourcePreviewResponse,
TabularSourceResponse,
)
from govoplan_connectors.backend.feeds import ConnectorFeedProvider, feed_rows
from govoplan_connectors.backend.feeds import (
FEED_PRIVATE_PUBLISH_SCOPE,
FEED_PUBLISH_SCOPE,
ConnectorFeedProvider,
feed_rows,
)
from govoplan_connectors.backend.governed_runtime import (
GovernedConnectorError,
create_configuration,
@@ -328,7 +335,9 @@ def api_render_feed(
payload: FeedRenderPayload,
principal: ApiPrincipal = Depends(get_api_principal),
) -> Response:
_require_any_scope(principal, READ_SCOPE, ADMIN_SCOPE)
_require_any_scope(principal, FEED_PUBLISH_SCOPE, ADMIN_SCOPE)
if payload.audience != "public":
_require_any_scope(principal, FEED_PRIVATE_PUBLISH_SCOPE, ADMIN_SCOPE)
try:
rendered = feed_transport.render(
FeedRenderRequest(
@@ -339,9 +348,9 @@ def api_render_feed(
description=payload.description,
language=payload.language,
entries=tuple(
FeedEntry(**item.model_dump()) for item in payload.entries
_publication_feed_entry(item) for item in payload.entries
),
allowed_visibilities=frozenset(payload.allowed_visibilities),
allowed_visibilities=_feed_audience_visibilities(payload.audience),
)
)
except FeedCapabilityError as exc:
@@ -352,10 +361,39 @@ def api_render_feed(
headers={
"X-GovOPlaN-Feed-Included": str(rendered.included_entries),
"X-GovOPlaN-Feed-Excluded": str(rendered.excluded_entries),
"X-GovOPlaN-Feed-Audience": payload.audience,
},
)
def _feed_audience_visibilities(
audience: str,
) -> frozenset[FeedVisibility]:
if audience == "public":
return frozenset({"public"})
if audience == "tenant":
return frozenset({"public", "tenant"})
return frozenset({"public", "tenant", "private"})
def _publication_feed_entry(item: FeedPublicationEntryPayload) -> FeedEntry:
values = item.model_dump(
exclude={"source_kind", "source_module", "source_ref", "source_revision"}
)
values["categories"] = tuple(values["categories"])
values["enclosures"] = tuple(values["enclosures"])
values["metadata"] = {
**values["metadata"],
"govoplan_source": {
"kind": item.source_kind,
"module": item.source_module,
"reference": item.source_ref,
"revision": item.source_revision,
},
}
return FeedEntry(**values)
@router.get("/tabular-sources", response_model=TabularSourceListResponse)
def api_list_tabular_sources(
query: str = Query(default="", max_length=200),