fix: align local-first resource boundaries
This commit is contained in:
@@ -52,6 +52,8 @@ pub struct WorkspaceTrashRequest {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MindmapTrashRequest {
|
||||
pub action: String,
|
||||
pub source_kind: Option<String>,
|
||||
pub root_uri: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -827,6 +829,7 @@ pub async fn mindmap_trash_action(
|
||||
State(state): State<AppState>,
|
||||
Extension(context): Extension<RequestContext>,
|
||||
Path((doc_id, mindmap_id)): Path<(String, String)>,
|
||||
Query(query): Query<MindmapLocalQuery>,
|
||||
Json(body): Json<MindmapTrashRequest>,
|
||||
) -> Result<(StatusCode, HeaderMap, Json<Value>), WebError> {
|
||||
let action = body.action.trim();
|
||||
@@ -845,6 +848,36 @@ pub async fn mindmap_trash_action(
|
||||
let user_id = current_user_id(&state, &context).await;
|
||||
let doc_id = require_id(&context, &doc_id, "docId")?;
|
||||
let mindmap_id = require_id(&context, &mindmap_id, "mindmapId")?;
|
||||
let local_source_kind = body
|
||||
.source_kind
|
||||
.as_deref()
|
||||
.or(query.source_kind.as_deref())
|
||||
.map(str::trim);
|
||||
if local_source_kind == Some("local_folder") {
|
||||
let root_uri = query
|
||||
.root_uri
|
||||
.as_deref()
|
||||
.or(body.root_uri.as_deref())
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.ok_or_else(|| {
|
||||
WebError::bad_request_code("local_folder_root_required", "缺少本地文件夹 rootUri")
|
||||
.with_context(&context)
|
||||
})?;
|
||||
ensure_local_workspace_access(&context, root_uri)
|
||||
.map_err(|error| error.with_context(&context))?;
|
||||
let local_action = if action == "restore" {
|
||||
"restore"
|
||||
} else {
|
||||
"purge"
|
||||
};
|
||||
let execution = execute_local_tree_command(root_uri, local_action, mindmap_id, None, None)
|
||||
.map_err(|error| error.with_context(&context))?;
|
||||
return Ok(ok_response(
|
||||
&context,
|
||||
annotate_resource_lifecycle_result(execution, command_name, "mindmap"),
|
||||
));
|
||||
}
|
||||
let workspace_id = fetch_document_workspace_id(&state, &context, doc_id).await;
|
||||
let command = resource_command(
|
||||
&context,
|
||||
@@ -1284,6 +1317,103 @@ mod tests {
|
||||
.expect("trash index");
|
||||
assert!(trash_index.contains("local-file:Page/map.mindmap.json"));
|
||||
|
||||
let restored = app()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("PATCH")
|
||||
.uri("/api/mindmap/local-md:Page~2FPage.md/local-file:Page%2Fmap.mindmap.json")
|
||||
.header("content-type", "application/json")
|
||||
.header("x-mnote-actor-id", "user_real")
|
||||
.header("x-mnote-actor-type", "user")
|
||||
.body(Body::from(
|
||||
json!({
|
||||
"action": "restore",
|
||||
"sourceKind": "local_folder",
|
||||
"rootUri": root_uri,
|
||||
})
|
||||
.to_string(),
|
||||
))
|
||||
.expect("request"),
|
||||
)
|
||||
.await
|
||||
.expect("restore response");
|
||||
let restore_status = restored.status();
|
||||
let restore_body = to_bytes(restored.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("restore body");
|
||||
let restored_payload: Value = serde_json::from_slice(&restore_body).expect("json");
|
||||
assert_eq!(
|
||||
restore_status,
|
||||
StatusCode::OK,
|
||||
"{}",
|
||||
String::from_utf8_lossy(&restore_body)
|
||||
);
|
||||
assert_eq!(restored_payload["result"]["sourceKind"], "local_folder");
|
||||
assert_eq!(
|
||||
restored_payload["result"]["canonicalCommand"],
|
||||
"tree.resource.restore"
|
||||
);
|
||||
assert!(root.join("Page").join("map.mindmap.json").exists());
|
||||
|
||||
let delete_again = app()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("DELETE")
|
||||
.uri(format!(
|
||||
"/api/mindmap/local-md:Page~2FPage.md/local-file:Page%2Fmap.mindmap.json?sourceKind=local_folder&rootUri={root_uri}"
|
||||
))
|
||||
.header("x-mnote-actor-id", "user_real")
|
||||
.header("x-mnote-actor-type", "user")
|
||||
.body(Body::empty())
|
||||
.expect("request"),
|
||||
)
|
||||
.await
|
||||
.expect("delete again response");
|
||||
assert_eq!(delete_again.status(), StatusCode::OK);
|
||||
let _ = to_bytes(delete_again.into_body(), usize::MAX).await;
|
||||
|
||||
let purged = app()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("PATCH")
|
||||
.uri("/api/mindmap/local-md:Page~2FPage.md/local-file:Page%2Fmap.mindmap.json")
|
||||
.header("content-type", "application/json")
|
||||
.header("x-mnote-actor-id", "user_real")
|
||||
.header("x-mnote-actor-type", "user")
|
||||
.body(Body::from(
|
||||
json!({
|
||||
"action": "purge",
|
||||
"sourceKind": "local_folder",
|
||||
"rootUri": root_uri,
|
||||
})
|
||||
.to_string(),
|
||||
))
|
||||
.expect("request"),
|
||||
)
|
||||
.await
|
||||
.expect("purge response");
|
||||
let purge_status = purged.status();
|
||||
let purge_body = to_bytes(purged.into_body(), usize::MAX)
|
||||
.await
|
||||
.expect("purge body");
|
||||
let purged_payload: Value = serde_json::from_slice(&purge_body).expect("json");
|
||||
assert_eq!(
|
||||
purge_status,
|
||||
StatusCode::OK,
|
||||
"{}",
|
||||
String::from_utf8_lossy(&purge_body)
|
||||
);
|
||||
assert_eq!(purged_payload["result"]["sourceKind"], "local_folder");
|
||||
assert_eq!(
|
||||
purged_payload["result"]["canonicalCommand"],
|
||||
"tree.resource.purge"
|
||||
);
|
||||
assert!(!root
|
||||
.join(".mnote")
|
||||
.join("trash")
|
||||
.join("map.mindmap.json")
|
||||
.exists());
|
||||
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user