use crate::regexes::COUCHDB_DB_RULE;
use awc::Client;
use serde::{de::DeserializeOwned, Serialize};
use std::fmt::Debug;
pub mod errors;
use crate::client::couchdb::actions::db::all_docs::get_all_docs;
use errors::DBDeleteError;
async fn post_request<T: Serialize + DeserializeOwned + Debug>(
client: &awc::Client,
uri: &str,
body: &Vec<T>,
) -> Result<bool, Box<dyn std::error::Error>> {
let json_body: serde_json::Value = serde_json::json!({ "docs": body });
match client.post(uri).send_json(&json_body).await {
Ok(mut response) => {
if response.status().as_u16() >= 400 {
let bytes: Vec<u8> = response.body().await?.iter().cloned().collect();
let error: serde_json::Value =
serde_json::from_str(std::str::from_utf8(bytes.as_slice()).unwrap()).unwrap();
Err(Box::new(DBDeleteError::new(&format!(
"Error with the request to {}: error: \n{}",
&uri, error
))))
} else {
Ok(true)
}
}
Err(error) => Err(Box::new(DBDeleteError::new(&format!(
"Error with the request to {}: error: \n{}",
uri, error
)))),
}
}
pub async fn delete_all<T: Clone + Serialize + DeserializeOwned + Debug>(
client: &awc::Client,
db_name: &str,
host: &str,
) -> Result<bool, Box<dyn std::error::Error>> {
let uri: String = format!("{}/{}/_bulk_docs", host, db_name,);
let skip: usize = 0;
let mut max_rows: usize = 1000;
let limit: usize = 100;
while skip + limit < max_rows {
println!("max rows {}", max_rows);
let all_docs = get_all_docs::<T>(client, db_name, host, skip, limit).await?;
max_rows = all_docs.total_rows;
let new_docs: Vec<serde_json::Value> = all_docs
.rows
.iter()
.map(|old| {
let json_string = serde_json::to_string(&old.doc).unwrap();
let mut json: serde_json::Value = serde_json::from_str(&json_string).unwrap();
let json_map: &mut serde_json::Map<String, serde_json::Value> =
json.as_object_mut().unwrap();
json_map.insert("_deleted".to_string(), serde_json::json!(true));
serde_json::Value::Object(json_map.to_owned())
})
.collect();
post_request(&client, &uri, &new_docs).await?;
}
Ok(true)
}
pub async fn delete_db(
client: &Client,
db_name: &str,
host: &str,
) -> Result<bool, Box<dyn std::error::Error>> {
if !COUCHDB_DB_RULE.is_match(db_name) {
return Err(Box::new(DBDeleteError::new(&format!(
"{} string doesn't respect the regex rule {}",
db_name,
COUCHDB_DB_RULE.as_str()
))));
}
let uri: String = format!("{}/{}", host, db_name,);
match client.delete(&uri).send().await {
Ok(mut response) => {
if response.status().as_u16() >= 400 {
let bytes: Vec<u8> = response.body().await?.iter().cloned().collect();
let error: serde_json::Value =
serde_json::from_str(std::str::from_utf8(bytes.as_slice()).unwrap()).unwrap();
Err(Box::new(DBDeleteError::new(&format!(
"Error with the request to {}: error: \n{}",
&uri, error
))))
} else {
Ok(true)
}
}
Err(error) => Err(Box::new(DBDeleteError::new(&format!(
"Error with the request to {}: error: \n{}",
host, error
)))),
}
}
#[cfg(test)]
mod tests {
extern crate actix_web;
extern crate wiremock;
use super::*;
use std::error::Error;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
#[actix_rt::test]
async fn delete_db_regex_error_test() {
let client: Client = Client::default();
let box_result: Box<dyn Error> = delete_db(&client, "Test", "").await.unwrap_err();
let result: &DBDeleteError = box_result.downcast_ref().unwrap();
assert_eq!(
format!("{}", result),
format!(
"DBDeleteError: {} string doesn't respect the regex rule {}",
"Test",
COUCHDB_DB_RULE.as_str()
)
);
let box_result: Box<dyn Error> = delete_db(&client, "testT", "").await.unwrap_err();
let result: &DBDeleteError = box_result.downcast_ref().unwrap();
assert_eq!(
format!("{}", result),
format!(
"DBDeleteError: {} string doesn't respect the regex rule {}",
"testT",
COUCHDB_DB_RULE.as_str()
)
)
}
#[actix_rt::test]
async fn delete_db_http_error_test() {
let mock_server = MockServer::start().await;
let data = r#"
{
"error": "test error"
}"#;
let response_template: ResponseTemplate =
ResponseTemplate::new(400).set_body_raw(data.as_bytes(), "application/json");
Mock::given(method("DELETE"))
.and(path("/test"))
.respond_with(response_template)
.mount(&mock_server)
.await;
let error: serde_json::Value = serde_json::from_str(data).unwrap();
let client: Client = Client::default();
let host: String = mock_server.uri();
let box_result: Box<dyn Error> = delete_db(&client, "test", &host).await.unwrap_err();
let result: &DBDeleteError = box_result.downcast_ref().unwrap();
assert_eq!(
format!("{}", result),
format!(
"DBDeleteError: Error with the request to {}: error: \n{}",
format!("{}/{}", host, "test"),
error
)
)
}
#[actix_rt::test]
async fn delete_db_test() {
let mock_server = MockServer::start().await;
Mock::given(method("DELETE"))
.and(path("/test"))
.respond_with(ResponseTemplate::new(200))
.mount(&mock_server)
.await;
let client: Client = Client::default();
let host: String = mock_server.uri();
println!("test host: {}", host);
assert_eq!(delete_db(&client, "test", &host).await.unwrap(), true);
}
}