couchdb_orm/client/couchdb/actions/db/delete/
mod.rs1use crate::regexes::COUCHDB_DB_RULE;
18use awc::Client;
19use serde::{de::DeserializeOwned, Serialize};
20use std::fmt::Debug;
21
22pub mod errors;
23
24use crate::client::couchdb::actions::db::all_docs::get_all_docs;
25use errors::DBDeleteError;
26
27async fn post_request<T: Serialize + DeserializeOwned + Debug>(
28 client: &awc::Client,
29 uri: &str,
30 body: &Vec<T>,
31) -> Result<bool, Box<dyn std::error::Error>> {
32 let json_body: serde_json::Value = serde_json::json!({ "docs": body });
33 match client.post(uri).send_json(&json_body).await {
34 Ok(mut response) => {
35 if response.status().as_u16() >= 400 {
37 let bytes: Vec<u8> = response.body().await?.iter().cloned().collect();
38 let error: serde_json::Value =
39 serde_json::from_str(std::str::from_utf8(bytes.as_slice()).unwrap()).unwrap();
40 Err(Box::new(DBDeleteError::new(&format!(
41 "Error with the request to {}: error: \n{}",
42 &uri, error
43 ))))
44 } else {
45 Ok(true)
46 }
47 }
48 Err(error) => Err(Box::new(DBDeleteError::new(&format!(
49 "Error with the request to {}: error: \n{}",
50 uri, error
51 )))),
52 }
53}
54
55pub async fn delete_all<T: Clone + Serialize + DeserializeOwned + Debug>(
56 client: &awc::Client,
57 db_name: &str,
58 host: &str,
59) -> Result<bool, Box<dyn std::error::Error>> {
60 let uri: String = format!("{}/{}/_bulk_docs", host, db_name,);
61 let skip: usize = 0;
62 let mut max_rows: usize = 1000;
63 let limit: usize = 100;
64 while skip + limit < max_rows {
67 println!("max rows {}", max_rows);
69 let all_docs = get_all_docs::<T>(client, db_name, host, skip, limit).await?;
72 max_rows = all_docs.total_rows;
74 let new_docs: Vec<serde_json::Value> = all_docs
75 .rows
76 .iter()
77 .map(|old| {
78 let json_string = serde_json::to_string(&old.doc).unwrap();
79 let mut json: serde_json::Value = serde_json::from_str(&json_string).unwrap();
80 let json_map: &mut serde_json::Map<String, serde_json::Value> =
81 json.as_object_mut().unwrap();
82 json_map.insert("_deleted".to_string(), serde_json::json!(true));
83 serde_json::Value::Object(json_map.to_owned())
85 })
86 .collect();
87 post_request(&client, &uri, &new_docs).await?;
89 }
90 Ok(true)
91}
92
93pub async fn delete_db(
94 client: &Client,
95 db_name: &str,
96 host: &str,
97) -> Result<bool, Box<dyn std::error::Error>> {
98 if !COUCHDB_DB_RULE.is_match(db_name) {
99 return Err(Box::new(DBDeleteError::new(&format!(
100 "{} string doesn't respect the regex rule {}",
101 db_name,
102 COUCHDB_DB_RULE.as_str()
103 ))));
104 }
105 let uri: String = format!("{}/{}", host, db_name,);
106 match client.delete(&uri).send().await {
107 Ok(mut response) => {
108 if response.status().as_u16() >= 400 {
110 let bytes: Vec<u8> = response.body().await?.iter().cloned().collect();
111 let error: serde_json::Value =
112 serde_json::from_str(std::str::from_utf8(bytes.as_slice()).unwrap()).unwrap();
113 Err(Box::new(DBDeleteError::new(&format!(
114 "Error with the request to {}: error: \n{}",
115 &uri, error
116 ))))
117 } else {
118 Ok(true)
119 }
120 }
121 Err(error) => Err(Box::new(DBDeleteError::new(&format!(
122 "Error with the request to {}: error: \n{}",
123 host, error
124 )))),
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 extern crate actix_web;
131 extern crate wiremock;
132
133 use super::*;
134 use std::error::Error;
135
136 use wiremock::matchers::{method, path};
137 use wiremock::{Mock, MockServer, ResponseTemplate};
138
139 #[actix_rt::test]
140 async fn delete_db_regex_error_test() {
141 let client: Client = Client::default();
142 let box_result: Box<dyn Error> = delete_db(&client, "Test", "").await.unwrap_err();
143 let result: &DBDeleteError = box_result.downcast_ref().unwrap();
144 assert_eq!(
145 format!("{}", result),
146 format!(
147 "DBDeleteError: {} string doesn't respect the regex rule {}",
148 "Test",
149 COUCHDB_DB_RULE.as_str()
150 )
151 );
152
153 let box_result: Box<dyn Error> = delete_db(&client, "testT", "").await.unwrap_err();
154 let result: &DBDeleteError = box_result.downcast_ref().unwrap();
155 assert_eq!(
156 format!("{}", result),
157 format!(
158 "DBDeleteError: {} string doesn't respect the regex rule {}",
159 "testT",
160 COUCHDB_DB_RULE.as_str()
161 )
162 )
163 }
164
165 #[actix_rt::test]
166 async fn delete_db_http_error_test() {
167 let mock_server = MockServer::start().await;
168 let data = r#"
170 {
171 "error": "test error"
172 }"#;
173
174 let response_template: ResponseTemplate =
175 ResponseTemplate::new(400).set_body_raw(data.as_bytes(), "application/json");
176 Mock::given(method("DELETE"))
177 .and(path("/test"))
178 .respond_with(response_template)
179 .mount(&mock_server)
180 .await;
181
182 let error: serde_json::Value = serde_json::from_str(data).unwrap();
183
184 let client: Client = Client::default();
185 let host: String = mock_server.uri();
186
187 let box_result: Box<dyn Error> = delete_db(&client, "test", &host).await.unwrap_err();
188 let result: &DBDeleteError = box_result.downcast_ref().unwrap();
189 assert_eq!(
190 format!("{}", result),
191 format!(
192 "DBDeleteError: Error with the request to {}: error: \n{}",
193 format!("{}/{}", host, "test"),
194 error
195 )
196 )
197 }
198
199 #[actix_rt::test]
200 async fn delete_db_test() {
201 let mock_server = MockServer::start().await;
202 Mock::given(method("DELETE"))
203 .and(path("/test"))
204 .respond_with(ResponseTemplate::new(200))
205 .mount(&mock_server)
206 .await;
207
208 let client: Client = Client::default();
209 let host: String = mock_server.uri();
210 println!("test host: {}", host);
211 assert_eq!(delete_db(&client, "test", &host).await.unwrap(), true);
212 }
213}