diff --git a/crates/handlers/src/admin/v1/users/deactivate.rs b/crates/handlers/src/admin/v1/users/deactivate.rs index 1f3700cfd..49c478b29 100644 --- a/crates/handlers/src/admin/v1/users/deactivate.rs +++ b/crates/handlers/src/admin/v1/users/deactivate.rs @@ -59,6 +59,12 @@ pub struct Request { erase: bool, } +impl Default for Request { + fn default() -> Self { + Self { erase: true } + } +} + pub fn doc(operation: TransformOperation) -> TransformOperation { operation .id("deactivateUser") @@ -86,8 +92,9 @@ pub async fn handler( }: CallContext, NoApi(mut rng): NoApi, id: UlidPathParam, - Json(params): Json, + body: Option>, ) -> Result>, RouteError> { + let Json(params) = body.unwrap_or_default(); let id = *id; let mut user = repo .user() @@ -125,8 +132,7 @@ mod tests { use crate::test_utils::{RequestBuilderExt, ResponseExt, TestState, setup}; - #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] - async fn test_deactivate_user(pool: PgPool) { + async fn test_deactivate_user_helper(pool: PgPool, erase: Option) { setup(); let mut state = TestState::from_pool(pool.clone()).await.unwrap(); let token = state.token_with_scope("urn:mas:admin").await; @@ -140,8 +146,13 @@ mod tests { repo.save().await.unwrap(); let request = Request::post(format!("/api/admin/v1/users/{}/deactivate", user.id)) - .bearer(&token) - .empty(); + .bearer(&token); + let request = match erase { + None => request.empty(), + Some(erase) => request.json(serde_json::json!({ + "erase": erase, + })), + }; let response = state.request(request).await; response.assert_status(StatusCode::OK); let body: serde_json::Value = response.json(); @@ -161,6 +172,52 @@ mod tests { .await .expect("Deactivation job to be scheduled"); assert_eq!(job["user_id"], serde_json::json!(user.id)); + assert_eq!(job["hs_erase"], serde_json::json!(erase.unwrap_or(true))); + } + + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_deactivate_user(pool: PgPool) { + test_deactivate_user_helper(pool, Option::None).await; + } + + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_deactivate_user_with_explicit_erase(pool: PgPool) { + test_deactivate_user_helper(pool, Option::Some(true)).await; + } + + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_deactivate_user_without_erase(pool: PgPool) { + test_deactivate_user_helper(pool, Option::Some(false)).await; + } + + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_deactivate_user_missing_erase(pool: PgPool) { + setup(); + let mut state = TestState::from_pool(pool.clone()).await.unwrap(); + let token = state.token_with_scope("urn:mas:admin").await; + + let mut repo = state.repository().await.unwrap(); + let user = repo + .user() + .add(&mut state.rng(), &state.clock, "alice".to_owned()) + .await + .unwrap(); + repo.save().await.unwrap(); + + let request = Request::post(format!("/api/admin/v1/users/{}/deactivate", user.id)) + .bearer(&token) + .json(serde_json::json!({})); + let response = state.request(request).await; + response.assert_status(StatusCode::UNPROCESSABLE_ENTITY); + + // It should have not scheduled a deactivation job for the user + let count: i64 = sqlx::query_scalar( + "SELECT COUNT(1) FROM queue_jobs WHERE queue_name = 'deactivate-user'", + ) + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(count, 0); } #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] diff --git a/docs/api/spec.json b/docs/api/spec.json index c0e2ee510..cb597e144 100644 --- a/docs/api/spec.json +++ b/docs/api/spec.json @@ -1367,7 +1367,7 @@ } } }, - "required": true + "required": false }, "responses": { "200": {