this repo has no description
1use crate::api::repo::record::utils::{CommitParams, RecordOp, commit_and_log};
2use crate::api::repo::record::write::prepare_repo_write;
3use crate::repo::tracking::TrackingBlockStore;
4use crate::state::AppState;
5use axum::{
6 Json,
7 extract::State,
8 http::{HeaderMap, StatusCode},
9 response::{IntoResponse, Response},
10};
11use cid::Cid;
12use jacquard::types::string::Nsid;
13use jacquard_repo::{commit::Commit, mst::Mst, storage::BlockStore};
14use serde::Deserialize;
15use serde_json::json;
16use std::str::FromStr;
17use std::sync::Arc;
18use tracing::error;
19
20#[derive(Deserialize)]
21pub struct DeleteRecordInput {
22 pub repo: String,
23 pub collection: String,
24 pub rkey: String,
25 #[serde(rename = "swapRecord")]
26 pub swap_record: Option<String>,
27 #[serde(rename = "swapCommit")]
28 pub swap_commit: Option<String>,
29}
30
31pub async fn delete_record(
32 State(state): State<AppState>,
33 headers: HeaderMap,
34 Json(input): Json<DeleteRecordInput>,
35) -> Response {
36 let (did, user_id, current_root_cid) =
37 match prepare_repo_write(&state, &headers, &input.repo).await {
38 Ok(res) => res,
39 Err(err_res) => return err_res,
40 };
41 if let Some(swap_commit) = &input.swap_commit
42 && Cid::from_str(swap_commit).ok() != Some(current_root_cid) {
43 return (
44 StatusCode::CONFLICT,
45 Json(json!({"error": "InvalidSwap", "message": "Repo has been modified"})),
46 )
47 .into_response();
48 }
49 let tracking_store = TrackingBlockStore::new(state.block_store.clone());
50 let commit_bytes = match tracking_store.get(¤t_root_cid).await {
51 Ok(Some(b)) => b,
52 _ => {
53 return (
54 StatusCode::INTERNAL_SERVER_ERROR,
55 Json(json!({"error": "InternalError", "message": "Commit block not found"})),
56 )
57 .into_response();
58 }
59 };
60 let commit = match Commit::from_cbor(&commit_bytes) {
61 Ok(c) => c,
62 _ => {
63 return (
64 StatusCode::INTERNAL_SERVER_ERROR,
65 Json(json!({"error": "InternalError", "message": "Failed to parse commit"})),
66 )
67 .into_response();
68 }
69 };
70 let mst = Mst::load(Arc::new(tracking_store.clone()), commit.data, None);
71 let collection_nsid = match input.collection.parse::<Nsid>() {
72 Ok(n) => n,
73 Err(_) => {
74 return (
75 StatusCode::BAD_REQUEST,
76 Json(json!({"error": "InvalidCollection"})),
77 )
78 .into_response();
79 }
80 };
81 let key = format!("{}/{}", collection_nsid, input.rkey);
82 if let Some(swap_record_str) = &input.swap_record {
83 let expected_cid = Cid::from_str(swap_record_str).ok();
84 let actual_cid = mst.get(&key).await.ok().flatten();
85 if expected_cid != actual_cid {
86 return (StatusCode::CONFLICT, Json(json!({"error": "InvalidSwap", "message": "Record has been modified or does not exist"}))).into_response();
87 }
88 }
89 let prev_record_cid = mst.get(&key).await.ok().flatten();
90 if prev_record_cid.is_none() {
91 return (StatusCode::OK, Json(json!({}))).into_response();
92 }
93 let new_mst = match mst.delete(&key).await {
94 Ok(m) => m,
95 Err(e) => {
96 error!("Failed to delete from MST: {:?}", e);
97 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": format!("Failed to delete from MST: {:?}", e)}))).into_response();
98 }
99 };
100 let new_mst_root = match new_mst.persist().await {
101 Ok(c) => c,
102 Err(e) => {
103 error!("Failed to persist MST: {:?}", e);
104 return (
105 StatusCode::INTERNAL_SERVER_ERROR,
106 Json(json!({"error": "InternalError", "message": "Failed to persist MST"})),
107 )
108 .into_response();
109 }
110 };
111 let op = RecordOp::Delete {
112 collection: input.collection,
113 rkey: input.rkey,
114 prev: prev_record_cid,
115 };
116 let mut relevant_blocks = std::collections::BTreeMap::new();
117 if new_mst.blocks_for_path(&key, &mut relevant_blocks).await.is_err() {
118 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to get new MST blocks for path"}))).into_response();
119 }
120 if mst.blocks_for_path(&key, &mut relevant_blocks).await.is_err() {
121 return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "InternalError", "message": "Failed to get old MST blocks for path"}))).into_response();
122 }
123 let mut written_cids = tracking_store.get_all_relevant_cids();
124 for cid in relevant_blocks.keys() {
125 if !written_cids.contains(cid) {
126 written_cids.push(*cid);
127 }
128 }
129 let written_cids_str = written_cids
130 .iter()
131 .map(|c| c.to_string())
132 .collect::<Vec<_>>();
133 if let Err(e) = commit_and_log(
134 &state,
135 CommitParams {
136 did: &did,
137 user_id,
138 current_root_cid: Some(current_root_cid),
139 prev_data_cid: Some(commit.data),
140 new_mst_root,
141 ops: vec![op],
142 blocks_cids: &written_cids_str,
143 },
144 )
145 .await
146 {
147 return (
148 StatusCode::INTERNAL_SERVER_ERROR,
149 Json(json!({"error": "InternalError", "message": e})),
150 )
151 .into_response();
152 };
153 (StatusCode::OK, Json(json!({}))).into_response()
154}