feat(core): define datasource visibility policy contract
Module Package Release / publish-packages (push) Failing after 6s
Module Package Release / publish-packages (push) Failing after 6s
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "govoplan-core"
|
||||
version = "0.1.19"
|
||||
version = "0.1.20"
|
||||
description = "Reusable GovOPlaN platform core, access, tenancy, and RBAC components."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -5,6 +5,7 @@ from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Literal, Protocol, runtime_checkable
|
||||
|
||||
from govoplan_core.core.access import PrincipalRef
|
||||
from govoplan_core.core.external_references import (
|
||||
SOURCE_AUTHORITY_MODES,
|
||||
SourceAuthorityMode,
|
||||
@@ -24,6 +25,7 @@ CAPABILITY_DATASOURCE_LIFECYCLE = "datasources.lifecycle"
|
||||
CAPABILITY_DATASOURCE_PUBLICATION = "datasources.publication"
|
||||
CAPABILITY_DATASOURCE_ORIGINS = "connectors.datasourceOrigins"
|
||||
CAPABILITY_DATASOURCE_ARTIFACT_BACKENDS = "datasources.artifactBackends"
|
||||
CAPABILITY_POLICY_DATASOURCE_VISIBILITY = "policy.datasourceVisibility"
|
||||
|
||||
DatasourceMode = Literal["live", "cached", "static"]
|
||||
DatasourceKind = Literal[
|
||||
@@ -38,6 +40,7 @@ DatasourceKind = Literal[
|
||||
]
|
||||
DatasourceShape = Literal["tabular", "document", "binary", "directory", "stream"]
|
||||
DatasourceConsistency = Literal["current", "live", "frozen"]
|
||||
DatasourceVisibilityAction = Literal["discover", "read"]
|
||||
DatasourcePublicationStatus = Literal[
|
||||
"published",
|
||||
"published_with_warnings",
|
||||
@@ -70,6 +73,7 @@ class DatasourceField:
|
||||
name: str
|
||||
data_type: str
|
||||
nullable: bool = True
|
||||
classification: str = "internal"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -90,6 +94,8 @@ class DatasourceGovernance:
|
||||
classification: str = "internal"
|
||||
privacy_profile_ref: str | None = None
|
||||
retention_policy_ref: str | None = None
|
||||
access_policy_ref: str | None = None
|
||||
visibility_policy: Mapping[str, object] = field(default_factory=dict)
|
||||
hold_refs: tuple[str, ...] = ()
|
||||
publication_state: str = "draft"
|
||||
transfer_agreement_ref: str | None = None
|
||||
@@ -160,6 +166,12 @@ class DatasourceGovernance:
|
||||
retention_policy_ref=_optional_governance_text(
|
||||
source.get("retention_policy_ref")
|
||||
),
|
||||
access_policy_ref=_optional_governance_text(
|
||||
source.get("access_policy_ref")
|
||||
),
|
||||
visibility_policy=_governance_mapping(
|
||||
source.get("visibility_policy")
|
||||
),
|
||||
hold_refs=_governance_texts(source.get("hold_refs")),
|
||||
publication_state=str(source.get("publication_state") or "draft"),
|
||||
transfer_agreement_ref=_optional_governance_text(
|
||||
@@ -191,6 +203,8 @@ class DatasourceGovernance:
|
||||
"classification": self.classification,
|
||||
"privacy_profile_ref": self.privacy_profile_ref,
|
||||
"retention_policy_ref": self.retention_policy_ref,
|
||||
"access_policy_ref": self.access_policy_ref,
|
||||
"visibility_policy": dict(self.visibility_policy),
|
||||
"hold_refs": list(self.hold_refs),
|
||||
"publication_state": self.publication_state,
|
||||
"transfer_agreement_ref": self.transfer_agreement_ref,
|
||||
@@ -203,6 +217,39 @@ class DatasourceGovernance:
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class DatasourceVisibilityPolicyRequest:
|
||||
tenant_id: str
|
||||
datasource_ref: str
|
||||
principal: PrincipalRef
|
||||
action: DatasourceVisibilityAction
|
||||
classification: str = "internal"
|
||||
policy_ref: str | None = None
|
||||
consistency: DatasourceConsistency = "current"
|
||||
materialization_ref: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class DatasourceVisibilityPolicyDecision:
|
||||
allowed: bool
|
||||
reason: str | None = None
|
||||
policies: tuple[Mapping[str, object], ...] = ()
|
||||
decision_ref: str | None = None
|
||||
provenance: Mapping[str, object] = field(default_factory=dict)
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class DatasourceVisibilityPolicyProvider(Protocol):
|
||||
"""Optionally tighten Datasources-owned local visibility policy."""
|
||||
|
||||
def decide_datasource_visibility(
|
||||
self,
|
||||
session: object,
|
||||
*,
|
||||
request: DatasourceVisibilityPolicyRequest,
|
||||
) -> DatasourceVisibilityPolicyDecision: ...
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class DatasourceDescriptor:
|
||||
ref: str
|
||||
@@ -659,6 +706,13 @@ def datasource_origins(registry: object | None) -> DatasourceOriginProvider | No
|
||||
return capability if isinstance(capability, DatasourceOriginProvider) else None
|
||||
|
||||
|
||||
def datasource_visibility_policy_provider(
|
||||
registry: object | None,
|
||||
) -> DatasourceVisibilityPolicyProvider | None:
|
||||
capability = _capability(registry, CAPABILITY_POLICY_DATASOURCE_VISIBILITY)
|
||||
return capability if isinstance(capability, DatasourceVisibilityPolicyProvider) else None
|
||||
|
||||
|
||||
def _capability(registry: object | None, name: str) -> object | None:
|
||||
if (
|
||||
registry is None
|
||||
@@ -695,6 +749,7 @@ __all__ = [
|
||||
"CAPABILITY_DATASOURCE_LIFECYCLE",
|
||||
"CAPABILITY_DATASOURCE_ORIGINS",
|
||||
"CAPABILITY_DATASOURCE_PUBLICATION",
|
||||
"CAPABILITY_POLICY_DATASOURCE_VISIBILITY",
|
||||
"DatasourceAccessError",
|
||||
"DatasourceArtifactReference",
|
||||
"DatasourceArtifactBackend",
|
||||
@@ -725,9 +780,14 @@ __all__ = [
|
||||
"DatasourceStageInput",
|
||||
"DatasourceUnavailableError",
|
||||
"DatasourceValidationError",
|
||||
"DatasourceVisibilityAction",
|
||||
"DatasourceVisibilityPolicyDecision",
|
||||
"DatasourceVisibilityPolicyProvider",
|
||||
"DatasourceVisibilityPolicyRequest",
|
||||
"datasource_catalogue",
|
||||
"datasource_artifact_backend_provider",
|
||||
"datasource_lifecycle",
|
||||
"datasource_origins",
|
||||
"datasource_publication",
|
||||
"datasource_visibility_policy_provider",
|
||||
]
|
||||
|
||||
@@ -8,6 +8,7 @@ from govoplan_core.core.datasources import (
|
||||
CAPABILITY_DATASOURCE_LIFECYCLE,
|
||||
CAPABILITY_DATASOURCE_ORIGINS,
|
||||
CAPABILITY_DATASOURCE_PUBLICATION,
|
||||
CAPABILITY_POLICY_DATASOURCE_VISIBILITY,
|
||||
DatasourceCatalogueProvider,
|
||||
DatasourceArtifactBackendProvider,
|
||||
DatasourceDescriptor,
|
||||
@@ -21,11 +22,14 @@ from govoplan_core.core.datasources import (
|
||||
DatasourcePublicationResult,
|
||||
DatasourceReadResult,
|
||||
DatasourceStage,
|
||||
DatasourceVisibilityPolicyDecision,
|
||||
DatasourceVisibilityPolicyProvider,
|
||||
datasource_catalogue,
|
||||
datasource_artifact_backend_provider,
|
||||
datasource_lifecycle,
|
||||
datasource_origins,
|
||||
datasource_publication,
|
||||
datasource_visibility_policy_provider,
|
||||
)
|
||||
from govoplan_core.core.modules import ModuleContext, ModuleManifest
|
||||
from govoplan_core.core.registry import PlatformRegistry
|
||||
@@ -161,6 +165,10 @@ class _Provider:
|
||||
def artifact_backends(self):
|
||||
return ()
|
||||
|
||||
def decide_datasource_visibility(self, session, *, request):
|
||||
del session, request
|
||||
return DatasourceVisibilityPolicyDecision(allowed=True)
|
||||
|
||||
|
||||
class DatasourceContractTests(unittest.TestCase):
|
||||
def test_capabilities_are_runtime_checkable_and_resolved_without_modules(self) -> None:
|
||||
@@ -170,6 +178,7 @@ class DatasourceContractTests(unittest.TestCase):
|
||||
self.assertIsInstance(provider, DatasourcePublicationProvider)
|
||||
self.assertIsInstance(provider, DatasourceOriginProvider)
|
||||
self.assertIsInstance(provider, DatasourceArtifactBackendProvider)
|
||||
self.assertIsInstance(provider, DatasourceVisibilityPolicyProvider)
|
||||
registry = PlatformRegistry()
|
||||
registry.register(
|
||||
ModuleManifest(
|
||||
@@ -182,6 +191,7 @@ class DatasourceContractTests(unittest.TestCase):
|
||||
CAPABILITY_DATASOURCE_PUBLICATION: lambda context: provider,
|
||||
CAPABILITY_DATASOURCE_ORIGINS: lambda context: provider,
|
||||
CAPABILITY_DATASOURCE_ARTIFACT_BACKENDS: lambda context: provider,
|
||||
CAPABILITY_POLICY_DATASOURCE_VISIBILITY: lambda context: provider,
|
||||
},
|
||||
)
|
||||
)
|
||||
@@ -192,6 +202,7 @@ class DatasourceContractTests(unittest.TestCase):
|
||||
self.assertIs(provider, datasource_publication(registry))
|
||||
self.assertIs(provider, datasource_origins(registry))
|
||||
self.assertIs(provider, datasource_artifact_backend_provider(registry))
|
||||
self.assertIs(provider, datasource_visibility_policy_provider(registry))
|
||||
self.assertIsNone(datasource_catalogue(PlatformRegistry()))
|
||||
|
||||
def test_descriptor_distinguishes_mode_kind_shape_and_materialization(self) -> None:
|
||||
|
||||
Generated
+4
-4
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.19",
|
||||
"version": "0.1.20",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.19",
|
||||
"version": "0.1.20",
|
||||
"dependencies": {
|
||||
"@govoplan/access-webui": "file:../../govoplan-access/webui",
|
||||
"@govoplan/addresses-webui": "file:../../govoplan-addresses/webui",
|
||||
@@ -288,7 +288,7 @@
|
||||
},
|
||||
"../../govoplan-datasources/webui": {
|
||||
"name": "@govoplan/datasources-webui",
|
||||
"version": "0.1.18",
|
||||
"version": "0.1.20",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
@@ -547,7 +547,7 @@
|
||||
},
|
||||
"../../govoplan-policy/webui": {
|
||||
"name": "@govoplan/policy-webui",
|
||||
"version": "0.1.18",
|
||||
"version": "0.1.19",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/core-webui",
|
||||
"version": "0.1.19",
|
||||
"version": "0.1.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
Reference in New Issue
Block a user