generated from coulomb/repo-seed
projection-only multifiling
This commit is contained in:
@@ -346,26 +346,12 @@ class ServiceRuntime:
|
||||
decision = mapper.access_point.decide_action(CMISAction.GET_CHILDREN, context)
|
||||
if not decision.allowed:
|
||||
raise _cmis_authorization_error(decision, "getChildren")
|
||||
projections = [
|
||||
projection.to_dict()
|
||||
for asset in self.repository.list_assets()
|
||||
if (
|
||||
projection := mapper.map_asset(
|
||||
asset,
|
||||
context,
|
||||
representations=self.repository.list_representations(asset_id=asset.id),
|
||||
versions=self.repository.list_versions(asset.id),
|
||||
relationship_ids=[
|
||||
f"cmis:relationship:{relationship.relationship_id}"
|
||||
for relationship in self.repository.list_relationships(source_id=asset.id)
|
||||
],
|
||||
metadata_records=self.repository.list_metadata_records(asset.id),
|
||||
)
|
||||
)
|
||||
]
|
||||
folder_path = _cmis_folder_path(folder_id)
|
||||
projections = self._cmis_children_for_folder(mapper, context, folder_path=folder_path)
|
||||
paged = projections[max(skip_count, 0) : max(skip_count, 0) + max(max_items, 0)]
|
||||
return {
|
||||
"folder_id": folder_id or mapper.access_point.root_folder_id,
|
||||
"folder_path": folder_path or "/",
|
||||
"objects": paged,
|
||||
"num_items": len(paged),
|
||||
"has_more_items": len(projections) > max(skip_count, 0) + len(paged),
|
||||
@@ -441,6 +427,26 @@ class ServiceRuntime:
|
||||
)
|
||||
return acl
|
||||
|
||||
def cmis_object_parents(
|
||||
self,
|
||||
access_point_id: str,
|
||||
object_id: str,
|
||||
context: OperationContext,
|
||||
) -> dict[str, Any]:
|
||||
mapper = self._cmis_mapper(access_point_id)
|
||||
decision = mapper.access_point.decide_action(CMISAction.GET_OBJECT_PARENTS, context, resource=object_id)
|
||||
if not decision.allowed:
|
||||
raise _cmis_authorization_error(decision, "getObjectParents")
|
||||
asset_id = _cmis_asset_id(object_id)
|
||||
asset = self.repository.get_asset(asset_id)
|
||||
if not mapper.access_point.exposes_asset(asset, context):
|
||||
raise NotFoundError(
|
||||
"CMIS object not found",
|
||||
details={"object_id": object_id, "access_point_id": access_point_id},
|
||||
)
|
||||
parents = list(mapper.parent_folders_for_asset(asset))
|
||||
return {"object_id": mapper.asset_object_id(asset.id), "parents": parents, "count": len(parents)}
|
||||
|
||||
def cmis_create_document(
|
||||
self,
|
||||
access_point_id: str,
|
||||
@@ -584,18 +590,14 @@ class ServiceRuntime:
|
||||
"supported": ["SELECT * FROM cmis:document", "SELECT * FROM kontextual:document"],
|
||||
},
|
||||
)
|
||||
children = self.cmis_children(
|
||||
access_point_id,
|
||||
context,
|
||||
skip_count=skip_count,
|
||||
max_items=max_items,
|
||||
)
|
||||
projections = self._cmis_document_projections(mapper, context)
|
||||
paged = projections[max(skip_count, 0) : max(skip_count, 0) + max(max_items, 0)]
|
||||
return {
|
||||
"query": query,
|
||||
"results": children["objects"],
|
||||
"num_items": children["num_items"],
|
||||
"has_more_items": children["has_more_items"],
|
||||
"total_num_items": children["total_num_items"],
|
||||
"results": paged,
|
||||
"num_items": len(paged),
|
||||
"has_more_items": len(projections) > max(skip_count, 0) + len(paged),
|
||||
"total_num_items": len(projections),
|
||||
}
|
||||
|
||||
def cmis_relationships(
|
||||
@@ -685,6 +687,74 @@ class ServiceRuntime:
|
||||
return self._cmis_asset_visible(mapper, relationship.target_id, context)
|
||||
return True
|
||||
|
||||
def _cmis_children_for_folder(
|
||||
self,
|
||||
mapper: CMISDomainMapper,
|
||||
context: OperationContext,
|
||||
*,
|
||||
folder_path: str | None,
|
||||
) -> list[dict[str, Any]]:
|
||||
assets = [
|
||||
asset
|
||||
for asset in self.repository.list_assets()
|
||||
if mapper.access_point.exposes_asset(asset, context)
|
||||
]
|
||||
if folder_path in (None, "/"):
|
||||
child_folder_paths = set()
|
||||
for asset in assets:
|
||||
for path in mapper.asset_paths(asset):
|
||||
first = path.strip("/").split("/")[0]
|
||||
if first:
|
||||
child_folder_paths.add("/" + first)
|
||||
return [mapper.folder_projection(path) for path in sorted(child_folder_paths)]
|
||||
children: list[dict[str, Any]] = []
|
||||
folder_path = _normalize_cmis_path(folder_path)
|
||||
child_folder_paths: set[str] = set()
|
||||
for asset in assets:
|
||||
for path in mapper.asset_paths(asset):
|
||||
parent = _path_parent(path)
|
||||
if parent == folder_path:
|
||||
projection = mapper.map_asset(
|
||||
asset,
|
||||
context,
|
||||
representations=self.repository.list_representations(asset_id=asset.id),
|
||||
versions=self.repository.list_versions(asset.id),
|
||||
relationship_ids=[
|
||||
f"cmis:relationship:{relationship.relationship_id}"
|
||||
for relationship in self.repository.list_relationships(source_id=asset.id)
|
||||
if self._cmis_relationship_visible(mapper, relationship, context)
|
||||
],
|
||||
metadata_records=self.repository.list_metadata_records(asset.id),
|
||||
)
|
||||
if projection is not None:
|
||||
children.append(projection.to_dict())
|
||||
elif _path_parent(parent) == folder_path:
|
||||
child_folder_paths.add(parent)
|
||||
return [mapper.folder_projection(path) for path in sorted(child_folder_paths)] + children
|
||||
|
||||
def _cmis_document_projections(
|
||||
self,
|
||||
mapper: CMISDomainMapper,
|
||||
context: OperationContext,
|
||||
) -> list[dict[str, Any]]:
|
||||
projections = []
|
||||
for asset in self.repository.list_assets():
|
||||
projection = mapper.map_asset(
|
||||
asset,
|
||||
context,
|
||||
representations=self.repository.list_representations(asset_id=asset.id),
|
||||
versions=self.repository.list_versions(asset.id),
|
||||
relationship_ids=[
|
||||
f"cmis:relationship:{relationship.relationship_id}"
|
||||
for relationship in self.repository.list_relationships(source_id=asset.id)
|
||||
if self._cmis_relationship_visible(mapper, relationship, context)
|
||||
],
|
||||
metadata_records=self.repository.list_metadata_records(asset.id),
|
||||
)
|
||||
if projection is not None:
|
||||
projections.append(projection.to_dict())
|
||||
return projections
|
||||
|
||||
def create_asset(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]:
|
||||
classification = Classification.from_dict(payload["classification"])
|
||||
result = self.asset_service().create_asset(
|
||||
@@ -2140,6 +2210,14 @@ def create_app(runtime: ServiceRuntime | None = None):
|
||||
) -> dict[str, Any]:
|
||||
return response(runtime.cmis_acl, access_point_id, object_id, context)
|
||||
|
||||
@app.get("/cmis/{access_point_id}/browser/parents/{object_id:path}", tags=["cmis"])
|
||||
def cmis_object_parents(
|
||||
access_point_id: str,
|
||||
object_id: str,
|
||||
context: OperationContext = Depends(context_from_headers),
|
||||
) -> dict[str, Any]:
|
||||
return response(runtime.cmis_object_parents, access_point_id, object_id, context)
|
||||
|
||||
@app.post("/cmis/{access_point_id}/browser/document", tags=["cmis"])
|
||||
def cmis_create_document(
|
||||
access_point_id: str,
|
||||
@@ -2632,6 +2710,29 @@ def _cmis_asset_id(object_id: str | None) -> str:
|
||||
return normalized
|
||||
|
||||
|
||||
def _cmis_folder_path(folder_id: str | None) -> str | None:
|
||||
if not folder_id:
|
||||
return None
|
||||
normalized = folder_id.strip()
|
||||
if normalized in {"cmis-root", "root", "/"}:
|
||||
return "/"
|
||||
if normalized.startswith("cmis:folder:"):
|
||||
return "/" + normalized.removeprefix("cmis:folder:").replace("::", "/")
|
||||
return _normalize_cmis_path(normalized)
|
||||
|
||||
|
||||
def _normalize_cmis_path(path: str) -> str:
|
||||
parts = [part.strip().strip("/") for part in path.replace("\\", "/").split("/") if part.strip("/")]
|
||||
return "/" + "/".join(parts)
|
||||
|
||||
|
||||
def _path_parent(path: str) -> str:
|
||||
parts = _normalize_cmis_path(path).strip("/").split("/")
|
||||
if len(parts) <= 1:
|
||||
return "/"
|
||||
return "/" + "/".join(parts[:-1])
|
||||
|
||||
|
||||
def _cmis_authorization_error(decision: PolicyDecision, operation: str) -> AuthorizationError:
|
||||
return AuthorizationError(
|
||||
"CMIS operation denied by access-point profile",
|
||||
|
||||
Reference in New Issue
Block a user