generated from coulomb/repo-seed
CMIS authoring operations
This commit is contained in:
@@ -16,6 +16,7 @@ from kontextual_engine.adapters.memory import InMemoryAssetRegistryRepository
|
||||
from kontextual_engine.core import (
|
||||
Actor,
|
||||
ActorType,
|
||||
AssetRepresentation,
|
||||
AuditEvent,
|
||||
AuditOutcome,
|
||||
Classification,
|
||||
@@ -33,6 +34,7 @@ from kontextual_engine.core import (
|
||||
PolicyDecision,
|
||||
PolicyEffect,
|
||||
RelationshipTargetKind,
|
||||
RepresentationKind,
|
||||
RetrievalFeedbackLabel,
|
||||
SourceReference,
|
||||
TransformationRunStatus,
|
||||
@@ -419,6 +421,127 @@ class ServiceRuntime:
|
||||
)
|
||||
return content_stream
|
||||
|
||||
def cmis_create_document(
|
||||
self,
|
||||
access_point_id: str,
|
||||
payload: dict[str, Any],
|
||||
context: OperationContext,
|
||||
) -> dict[str, Any]:
|
||||
mapper = self._cmis_mapper(access_point_id)
|
||||
decision = mapper.access_point.decide_action(CMISAction.CREATE_DOCUMENT, context)
|
||||
if not decision.allowed:
|
||||
raise _cmis_authorization_error(decision, "createDocument")
|
||||
classification = Classification.from_dict(
|
||||
{
|
||||
"asset_type": payload.get("asset_type", "document"),
|
||||
"sensitivity": payload.get("sensitivity", "internal"),
|
||||
"owner": payload.get("owner"),
|
||||
"topics": payload.get("topics", []),
|
||||
"metadata": dict(payload.get("classification_metadata", {})),
|
||||
}
|
||||
)
|
||||
content = payload.get("content")
|
||||
representations = []
|
||||
if content is not None:
|
||||
representations.append(
|
||||
AssetRepresentation.from_content(
|
||||
payload.get("asset_id") or "cmis-new-document",
|
||||
RepresentationKind.SOURCE,
|
||||
payload.get("media_type", "text/plain"),
|
||||
content,
|
||||
storage_ref=payload.get("storage_ref"),
|
||||
)
|
||||
)
|
||||
result = self.asset_service().create_asset(
|
||||
payload["name"],
|
||||
classification,
|
||||
context,
|
||||
asset_id=payload.get("asset_id"),
|
||||
representations=representations,
|
||||
metadata_records=[_metadata_record(item) for item in payload.get("metadata_records", [])],
|
||||
idempotency_key=payload.get("idempotency_key"),
|
||||
)
|
||||
return self.cmis_object(access_point_id, mapper.asset_object_id(result.asset.id), context)
|
||||
|
||||
def cmis_update_properties(
|
||||
self,
|
||||
access_point_id: str,
|
||||
object_id: str,
|
||||
payload: dict[str, Any],
|
||||
context: OperationContext,
|
||||
) -> dict[str, Any]:
|
||||
mapper = self._cmis_mapper(access_point_id)
|
||||
decision = mapper.access_point.decide_action(CMISAction.UPDATE_PROPERTIES, context, resource=object_id)
|
||||
if not decision.allowed:
|
||||
raise _cmis_authorization_error(decision, "updateProperties")
|
||||
asset_id = _cmis_asset_id(object_id)
|
||||
properties = dict(payload.get("properties", payload))
|
||||
expected = properties.pop("expected_current_version_id", payload.get("expected_current_version_id", None))
|
||||
for key, value in properties.items():
|
||||
if key.startswith("cmis:"):
|
||||
continue
|
||||
self.asset_service().add_metadata_record(
|
||||
asset_id,
|
||||
MetadataRecord(key=_cmis_metadata_key(key), value=value, confirmed=bool(payload.get("confirmed", True))),
|
||||
context,
|
||||
expected_current_version_id=expected,
|
||||
)
|
||||
expected = None
|
||||
return self.cmis_object(access_point_id, object_id, context)
|
||||
|
||||
def cmis_set_content_stream(
|
||||
self,
|
||||
access_point_id: str,
|
||||
object_id: str,
|
||||
payload: dict[str, Any],
|
||||
context: OperationContext,
|
||||
) -> dict[str, Any]:
|
||||
mapper = self._cmis_mapper(access_point_id)
|
||||
decision = mapper.access_point.decide_action(CMISAction.SET_CONTENT_STREAM, context, resource=object_id)
|
||||
if not decision.allowed:
|
||||
raise _cmis_authorization_error(decision, "setContentStream")
|
||||
asset_id = _cmis_asset_id(object_id)
|
||||
representation = AssetRepresentation.from_content(
|
||||
asset_id,
|
||||
payload.get("kind", RepresentationKind.SOURCE.value),
|
||||
payload.get("media_type", "text/plain"),
|
||||
payload.get("content", ""),
|
||||
storage_ref=payload.get("storage_ref"),
|
||||
)
|
||||
self.asset_service().add_representation(
|
||||
asset_id,
|
||||
representation,
|
||||
context,
|
||||
expected_current_version_id=payload.get("expected_current_version_id"),
|
||||
)
|
||||
return self.cmis_object(access_point_id, object_id, context)
|
||||
|
||||
def cmis_delete_object(
|
||||
self,
|
||||
access_point_id: str,
|
||||
object_id: str,
|
||||
payload: dict[str, Any],
|
||||
context: OperationContext,
|
||||
) -> dict[str, Any]:
|
||||
mapper = self._cmis_mapper(access_point_id)
|
||||
decision = mapper.access_point.decide_action(CMISAction.DELETE_OBJECT, context, resource=object_id)
|
||||
if not decision.allowed:
|
||||
raise _cmis_authorization_error(decision, "deleteObject")
|
||||
asset_id = _cmis_asset_id(object_id)
|
||||
result = self.asset_service().request_delete(
|
||||
asset_id,
|
||||
context,
|
||||
expected_current_version_id=payload.get("expected_current_version_id"),
|
||||
)
|
||||
return {
|
||||
"object_id": mapper.asset_object_id(asset_id),
|
||||
"deleted": False,
|
||||
"lifecycle": result.asset.lifecycle.value,
|
||||
"version": result.version.to_dict(),
|
||||
"audit_event": result.audit_event.to_dict(),
|
||||
"policy_decision": result.policy_decision.to_dict(),
|
||||
}
|
||||
|
||||
def cmis_query(
|
||||
self,
|
||||
access_point_id: str,
|
||||
@@ -1964,6 +2087,41 @@ def create_app(runtime: ServiceRuntime | None = None):
|
||||
) -> dict[str, Any]:
|
||||
return response(runtime.cmis_content_stream, access_point_id, object_id, context)
|
||||
|
||||
@app.post("/cmis/{access_point_id}/browser/document", tags=["cmis"])
|
||||
def cmis_create_document(
|
||||
access_point_id: str,
|
||||
payload: dict[str, Any],
|
||||
context: OperationContext = Depends(context_from_headers),
|
||||
) -> dict[str, Any]:
|
||||
return response(runtime.cmis_create_document, access_point_id, payload, context)
|
||||
|
||||
@app.post("/cmis/{access_point_id}/browser/object/{object_id:path}/properties", tags=["cmis"])
|
||||
def cmis_update_properties(
|
||||
access_point_id: str,
|
||||
object_id: str,
|
||||
payload: dict[str, Any],
|
||||
context: OperationContext = Depends(context_from_headers),
|
||||
) -> dict[str, Any]:
|
||||
return response(runtime.cmis_update_properties, access_point_id, object_id, payload, context)
|
||||
|
||||
@app.post("/cmis/{access_point_id}/browser/object/{object_id:path}/content", tags=["cmis"])
|
||||
def cmis_set_content_stream(
|
||||
access_point_id: str,
|
||||
object_id: str,
|
||||
payload: dict[str, Any],
|
||||
context: OperationContext = Depends(context_from_headers),
|
||||
) -> dict[str, Any]:
|
||||
return response(runtime.cmis_set_content_stream, access_point_id, object_id, payload, context)
|
||||
|
||||
@app.post("/cmis/{access_point_id}/browser/object/{object_id:path}/delete", tags=["cmis"])
|
||||
def cmis_delete_object(
|
||||
access_point_id: str,
|
||||
object_id: str,
|
||||
payload: dict[str, Any] | None = None,
|
||||
context: OperationContext = Depends(context_from_headers),
|
||||
) -> dict[str, Any]:
|
||||
return response(runtime.cmis_delete_object, access_point_id, object_id, payload or {}, context)
|
||||
|
||||
@app.get("/cmis/{access_point_id}/browser/query", tags=["cmis"])
|
||||
def cmis_query(
|
||||
access_point_id: str,
|
||||
@@ -2442,6 +2600,14 @@ def _cmis_change_type(operation: str) -> str:
|
||||
return "security"
|
||||
|
||||
|
||||
def _cmis_metadata_key(key: str) -> str:
|
||||
if key.startswith("kontextual:metadata:"):
|
||||
return key.removeprefix("kontextual:metadata:")
|
||||
if key.startswith("kontextual:"):
|
||||
return key.removeprefix("kontextual:")
|
||||
return key
|
||||
|
||||
|
||||
def _age_seconds(start: str, end: str) -> float:
|
||||
try:
|
||||
start_dt = datetime.fromisoformat(start.replace("Z", "+00:00"))
|
||||
|
||||
Reference in New Issue
Block a user