feat: implement governed datasource catalogue metadata
This commit is contained in:
@@ -9,6 +9,7 @@ from govoplan_core.core.datasources import (
|
||||
DatasourceAccessError,
|
||||
DatasourceDescriptor,
|
||||
DatasourceError,
|
||||
DatasourceGovernance,
|
||||
DatasourceMaterialization,
|
||||
DatasourceNotFoundError,
|
||||
DatasourceOrigin,
|
||||
@@ -23,6 +24,8 @@ from govoplan_datasources.backend.runtime import get_registry
|
||||
from govoplan_datasources.backend.schemas import (
|
||||
DatasourceFieldResponse,
|
||||
DatasourceFreezeRequest,
|
||||
DatasourceGovernancePayload,
|
||||
DatasourceGovernanceUpdateRequest,
|
||||
DatasourceListResponse,
|
||||
DatasourceMaterializationListResponse,
|
||||
DatasourceMaterializationResponse,
|
||||
@@ -128,6 +131,7 @@ def api_register_origin(
|
||||
source_name=payload.source_name,
|
||||
mode=payload.mode,
|
||||
description=payload.description,
|
||||
governance=_governance(payload.governance),
|
||||
)
|
||||
except DatasourceError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
@@ -204,6 +208,7 @@ def api_create_stage(
|
||||
"source_format": payload.format,
|
||||
},
|
||||
metadata=payload.metadata,
|
||||
governance=_governance(payload.governance),
|
||||
),
|
||||
)
|
||||
except DatasourceError as exc:
|
||||
@@ -270,6 +275,13 @@ def api_promote_stage(
|
||||
def api_list_datasources(
|
||||
query: str = Query(default="", max_length=200),
|
||||
limit: int = Query(default=100, ge=1, le=100),
|
||||
authority_mode: str | None = Query(default=None, max_length=40),
|
||||
classification: str | None = Query(default=None, max_length=80),
|
||||
publication_state: str | None = Query(default=None, max_length=50),
|
||||
owner_ref: str | None = Query(default=None, max_length=500),
|
||||
responsible_organization_ref: str | None = Query(default=None, max_length=500),
|
||||
affected_ref: str | None = Query(default=None, max_length=1000),
|
||||
dependency_ref: str | None = Query(default=None, max_length=1000),
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DatasourceListResponse:
|
||||
@@ -280,6 +292,13 @@ def api_list_datasources(
|
||||
principal,
|
||||
query=query,
|
||||
limit=limit,
|
||||
authority_mode=authority_mode,
|
||||
classification=classification,
|
||||
publication_state=publication_state,
|
||||
owner_ref=owner_ref,
|
||||
responsible_organization_ref=responsible_organization_ref,
|
||||
affected_ref=affected_ref,
|
||||
dependency_ref=dependency_ref,
|
||||
)
|
||||
except DatasourceError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
@@ -311,6 +330,39 @@ def api_get_datasource(
|
||||
return _datasource_response(item)
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/{datasource_id}/governance",
|
||||
response_model=DatasourceResponse,
|
||||
)
|
||||
def api_update_datasource_governance(
|
||||
datasource_id: str,
|
||||
payload: DatasourceGovernanceUpdateRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DatasourceResponse:
|
||||
_require_any_scope(principal, SOURCE_WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
item = _provider().update_datasource_governance(
|
||||
session,
|
||||
principal,
|
||||
datasource_ref=f"datasource:{datasource_id}",
|
||||
governance=_governance(payload.governance)
|
||||
or DatasourceGovernance(),
|
||||
)
|
||||
except DatasourceError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
_audit(
|
||||
session,
|
||||
principal,
|
||||
action="datasources.governance.updated",
|
||||
object_type="datasource",
|
||||
object_id=item.ref,
|
||||
details={"governance": item.governance.to_dict()},
|
||||
)
|
||||
session.commit()
|
||||
return _datasource_response(item)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{datasource_id}/preview",
|
||||
response_model=DatasourcePreviewResponse,
|
||||
@@ -509,6 +561,7 @@ def _datasource_response(item: DatasourceDescriptor) -> DatasourceResponse:
|
||||
capabilities=list(item.capabilities),
|
||||
provenance=dict(item.provenance),
|
||||
metadata=dict(item.metadata),
|
||||
governance=item.governance.to_dict(),
|
||||
)
|
||||
|
||||
|
||||
@@ -539,6 +592,7 @@ def _materialization_response(
|
||||
created_at=item.created_at.isoformat() if item.created_at else None,
|
||||
provenance=dict(item.provenance),
|
||||
metadata=dict(item.metadata),
|
||||
governance=item.governance.to_dict(),
|
||||
)
|
||||
|
||||
|
||||
@@ -569,6 +623,7 @@ def _stage_response(item: DatasourceStage) -> DatasourceStageResponse:
|
||||
promoted_materialization_ref=item.promoted_materialization_ref,
|
||||
provenance=dict(item.provenance),
|
||||
metadata=dict(item.metadata),
|
||||
governance=item.governance.to_dict(),
|
||||
)
|
||||
|
||||
|
||||
@@ -600,6 +655,14 @@ def _origin_response(item: DatasourceOrigin) -> DatasourceOriginResponse:
|
||||
)
|
||||
|
||||
|
||||
def _governance(
|
||||
payload: DatasourceGovernancePayload | None,
|
||||
) -> DatasourceGovernance | None:
|
||||
if payload is None:
|
||||
return None
|
||||
return DatasourceGovernance.from_mapping(payload.model_dump())
|
||||
|
||||
|
||||
def _audit(
|
||||
session: Session,
|
||||
principal: ApiPrincipal,
|
||||
|
||||
Reference in New Issue
Block a user