-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added hard and soft delete for servers
- Loading branch information
1 parent
b2dc653
commit e7eb3d7
Showing
10 changed files
with
304 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
use actix_web::{ | ||
web::{Data, Path}, | ||
HttpResponse, | ||
}; | ||
use chrono::Utc; | ||
use sqlx::PgPool; | ||
use uuid::Uuid; | ||
|
||
use crate::storage::{get_server_by_id, hard_delete_server, soft_delete_server}; | ||
|
||
#[tracing::instrument( | ||
name = "Soft Deleting Server", | ||
skip(server_id, db_pool), | ||
fields( | ||
id = %server_id, | ||
) | ||
)] | ||
pub async fn soft_delete(server_id: Path<Uuid>, db_pool: Data<PgPool>) -> HttpResponse { | ||
let now = Utc::now(); | ||
let id = server_id.into_inner(); | ||
|
||
match get_server_by_id(&db_pool, id).await { | ||
Ok(server) => { | ||
if server.deleted_at().is_some() { | ||
let err = format!("server {} has already been soft deleted", id); | ||
tracing::error!(err); | ||
HttpResponse::BadRequest().body(err) | ||
} else { | ||
match soft_delete_server(&db_pool, id, now).await { | ||
Ok(_) => { | ||
tracing::info!("server {} successfully soft deleted", id); | ||
HttpResponse::Ok().finish() | ||
} | ||
Err(e) => { | ||
let err = format!("failed to soft delete server {}: {}", id, e); | ||
tracing::error!(err); | ||
HttpResponse::InternalServerError().finish() | ||
} | ||
} | ||
} | ||
} | ||
Err(e) => match e { | ||
sqlx::Error::RowNotFound => { | ||
let err = format!("server {} not found", id); | ||
tracing::error!(err); | ||
HttpResponse::NotFound().body(err) | ||
} | ||
e => { | ||
let err = format!("failed to soft delete server {}: {}", id, e); | ||
tracing::error!(err); | ||
HttpResponse::InternalServerError().body(err) | ||
} | ||
}, | ||
} | ||
} | ||
|
||
#[tracing::instrument( | ||
name = "Hard Deleting Server", | ||
skip(server_id, db_pool), | ||
fields( | ||
id = %server_id, | ||
) | ||
)] | ||
pub async fn hard_delete(server_id: Path<Uuid>, db_pool: Data<PgPool>) -> HttpResponse { | ||
let id = server_id.into_inner(); | ||
match hard_delete_server(&db_pool, id).await { | ||
Ok(_) => { | ||
tracing::info!("server {} successfully hard deleted", id); | ||
HttpResponse::Ok().finish() | ||
} | ||
Err(e) => match e { | ||
sqlx::Error::RowNotFound => { | ||
let err = format!("server {} not found", id); | ||
tracing::error!(err); | ||
HttpResponse::NotFound().body(err) | ||
} | ||
e => { | ||
let err = format!("failed to hard delete server {}: {}", id, e); | ||
tracing::error!(err); | ||
HttpResponse::InternalServerError().body(err) | ||
} | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
mod create; | ||
mod delete; | ||
mod update; | ||
|
||
pub use create::*; | ||
pub use delete::*; | ||
pub use update::*; | ||
|
||
pub const BASE_PATH: &str = "/servers"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
use crate::utils::{ | ||
app::TestApp, | ||
http_client::{ContentType, Header, Path}, | ||
}; | ||
use chrono::{Days, Utc}; | ||
use claim::{assert_err, assert_none, assert_some}; | ||
use muttr_server::handlers::server::BASE_PATH; | ||
use uuid::Uuid; | ||
|
||
#[actix::test] | ||
async fn test_soft_delete_success() { | ||
let mut app = TestApp::spawn().await; | ||
|
||
let user = app | ||
.database | ||
.insert_user("[email protected]", "test.user", true) | ||
.await; | ||
let mut server = app.database.insert_server(user.id()).await; | ||
|
||
assert_none!( | ||
server.deleted_at(), | ||
"Server not initialized with None value for deleted_at", | ||
); | ||
|
||
let now = Utc::now(); | ||
let response = app | ||
.client | ||
.request( | ||
Path::DELETE(format!("{}/{}", BASE_PATH, server.id())), | ||
&[Header::ContentType(ContentType::Json)], | ||
None::<String>, | ||
) | ||
.await; | ||
|
||
assert_eq!( | ||
200, | ||
response.status(), | ||
"The API did not return 200 on valid server soft delete", | ||
); | ||
|
||
server = match app.database.get_server_by_id(server.id()).await { | ||
Ok(server) => server, | ||
Err(e) => panic!( | ||
"failed to retrieve server {} from database: {}", | ||
server.id(), | ||
e | ||
), | ||
}; | ||
let deleted_at = assert_some!(server.deleted_at(), "Server deleted_at is None"); | ||
let day = Days::new(1); | ||
assert!( | ||
deleted_at > now.checked_sub_days(day).unwrap() | ||
&& deleted_at < now.checked_add_days(day).unwrap(), | ||
"Server's deleted_at time was incorrect", | ||
); | ||
} | ||
|
||
#[actix::test] | ||
async fn test_soft_delete_failure() { | ||
let mut app = TestApp::spawn().await; | ||
|
||
let id = Uuid::new_v4(); | ||
let mut response = app | ||
.client | ||
.request( | ||
Path::DELETE(format!("{}/{}", BASE_PATH, id)), | ||
&[Header::ContentType(ContentType::Json)], | ||
None::<String>, | ||
) | ||
.await; | ||
|
||
assert_eq!( | ||
404, | ||
response.status(), | ||
"The APII did not return 404 when trying to soft delete a non-existant server", | ||
); | ||
|
||
let user = app | ||
.database | ||
.insert_user("[email protected]", "test.user", true) | ||
.await; | ||
let server = app.database.insert_server(user.id()).await; | ||
|
||
response = app | ||
.client | ||
.request( | ||
Path::DELETE(format!("{}/{}", BASE_PATH, server.id())), | ||
&[Header::ContentType(ContentType::Json)], | ||
None::<String>, | ||
) | ||
.await; | ||
|
||
assert_eq!( | ||
200, | ||
response.status(), | ||
"Unexpectedly failed to soft delete test server: {}", | ||
response.text().await.unwrap_or_default(), | ||
); | ||
|
||
response = app | ||
.client | ||
.request( | ||
Path::DELETE(format!("{}/{}", BASE_PATH, server.id())), | ||
&[Header::ContentType(ContentType::Json)], | ||
None::<String>, | ||
) | ||
.await; | ||
|
||
assert_eq!( | ||
400, | ||
response.status(), | ||
"The API did not return 400 when trying to soft delete a server that has already been soft deleted", | ||
); | ||
|
||
assert_eq!( | ||
format!("server {} has already been soft deleted", server.id()), | ||
response.text().await.unwrap_or_default(), | ||
"The response body did not match expected text" | ||
); | ||
} | ||
|
||
#[actix::test] | ||
async fn test_hard_delete_success() { | ||
let mut app = TestApp::spawn().await; | ||
|
||
let user = app | ||
.database | ||
.insert_user("[email protected]", "test.user", true) | ||
.await; | ||
let server = app.database.insert_server(user.id()).await; | ||
|
||
let response = app | ||
.client | ||
.request( | ||
Path::DELETE(format!("{}/{}/hard", BASE_PATH, server.id())), | ||
&[Header::ContentType(ContentType::Json)], | ||
None::<String>, | ||
) | ||
.await; | ||
|
||
assert_eq!( | ||
200, | ||
response.status(), | ||
"The API did not return 200 on valid server hard delete: {}", | ||
response.text().await.unwrap_or_default(), | ||
); | ||
|
||
assert_err!( | ||
app.database.get_server_by_id(server.id()).await, | ||
"Server {} still exists in the database", | ||
server.id(), | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
mod create; | ||
mod delete; | ||
mod update; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters