From 06a5a22032680162b9e3aae144de6b4997026491 Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Sun, 1 Sep 2024 00:51:05 +0800 Subject: [PATCH] feat: delete user --- ...22939e80ec2e75747503f0493d081ba43e4bd.json | 14 +++++++++ libs/client-api/src/http.rs | 13 +++++++++ libs/database/src/user.rs | 21 ++++++++++++++ src/api/user.rs | 12 +++++++- src/biz/user/user_info.rs | 4 +++ tests/user/delete.rs | 29 +++++++++++++++++++ 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 .sqlx/query-e6acbe78f0e8f776901c560088222939e80ec2e75747503f0493d081ba43e4bd.json diff --git a/.sqlx/query-e6acbe78f0e8f776901c560088222939e80ec2e75747503f0493d081ba43e4bd.json b/.sqlx/query-e6acbe78f0e8f776901c560088222939e80ec2e75747503f0493d081ba43e4bd.json new file mode 100644 index 00000000..2bb9c80d --- /dev/null +++ b/.sqlx/query-e6acbe78f0e8f776901c560088222939e80ec2e75747503f0493d081ba43e4bd.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM auth.users WHERE id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "e6acbe78f0e8f776901c560088222939e80ec2e75747503f0493d081ba43e4bd" +} diff --git a/libs/client-api/src/http.rs b/libs/client-api/src/http.rs index 18314a6e..fd944373 100644 --- a/libs/client-api/src/http.rs +++ b/libs/client-api/src/http.rs @@ -738,6 +738,19 @@ impl Client { AppResponse::<()>::from_response(resp).await?.into_error() } + /// Deletes the user account and all associated data. + #[instrument(level = "info", skip_all, err)] + pub async fn delete_user(&self) -> Result<(), AppResponseError> { + let url = format!("{}/api/user", self.base_url); + let resp = self + .http_client_with_auth(Method::DELETE, &url) + .await? + .send() + .await?; + log_request_id(&resp); + AppResponse::<()>::from_response(resp).await?.into_error() + } + pub async fn get_snapshot_list( &self, workspace_id: &str, diff --git a/libs/database/src/user.rs b/libs/database/src/user.rs index 4ea6f7c9..599caa84 100644 --- a/libs/database/src/user.rs +++ b/libs/database/src/user.rs @@ -240,3 +240,24 @@ pub async fn select_name_from_uuid(pool: &PgPool, user_uuid: &Uuid) -> Result Result<(), AppError> { + let res = sqlx::query!( + r#" + DELETE FROM auth.users WHERE id = $1 + "#, + user_uuid + ) + .execute(pool) + .await?; + + if res.rows_affected() != 1 { + return Err(AppError::RecordNotFound(format!( + "User with UUID {} not found", + user_uuid + ))); + } + + Ok(()) +} diff --git a/src/api/user.rs b/src/api/user.rs index f866223f..af0ff84c 100644 --- a/src/api/user.rs +++ b/src/api/user.rs @@ -1,4 +1,4 @@ -use crate::biz::user::user_info::{get_profile, get_user_workspace_info, update_user}; +use crate::biz::user::user_info::{delete_user, get_profile, get_user_workspace_info, update_user}; use crate::biz::user::user_verify::verify_token; use crate::state::AppState; use actix_web::web::{Data, Json}; @@ -16,6 +16,7 @@ pub fn user_scope() -> Scope { .service(web::resource("/update").route(web::post().to(update_user_handler))) .service(web::resource("/profile").route(web::get().to(get_user_profile_handler))) .service(web::resource("/workspace").route(web::get().to(get_user_workspace_info_handler))) + .service(web::resource("").route(web::delete().to(delete_user_handler))) } #[tracing::instrument(skip(state, path), err)] @@ -61,3 +62,12 @@ async fn update_user_handler( update_user(&state.pg_pool, auth.uuid()?, params).await?; Ok(AppResponse::Ok().into()) } + +#[tracing::instrument(skip(state), err)] +async fn delete_user_handler( + auth: Authorization, + state: Data, +) -> Result> { + delete_user(&state.pg_pool, auth.uuid()?).await?; + Ok(AppResponse::Ok().into()) +} diff --git a/src/biz/user/user_info.rs b/src/biz/user/user_info.rs index 470e39a4..6ae58dad 100644 --- a/src/biz/user/user_info.rs +++ b/src/biz/user/user_info.rs @@ -74,3 +74,7 @@ pub async fn update_user( let metadata = params.metadata.map(|m| json!(m.into_inner())); Ok(database::user::update_user(pg_pool, &user_uuid, params.name, params.email, metadata).await?) } + +pub async fn delete_user(pg_pool: &PgPool, user_uuid: Uuid) -> Result<(), AppResponseError> { + Ok(database::user::delete_user(pg_pool, &user_uuid).await?) +} diff --git a/tests/user/delete.rs b/tests/user/delete.rs index c8324ed7..fe451a52 100644 --- a/tests/user/delete.rs +++ b/tests/user/delete.rs @@ -1,6 +1,35 @@ use client_api_test::*; use gotrue::params::{AdminDeleteUserParams, AdminUserParams}; +#[tokio::test] +async fn user_delete_self() { + let (client, user) = generate_unique_registered_user_client().await; + let admin_client = admin_user_client().await; + { + // user found before deletion + let search_result = admin_client + .admin_list_users(Some(&user.email)) + .await + .unwrap(); + let _target_user = search_result + .into_iter() + .find(|u| u.email == user.email) + .unwrap(); + } + + client.delete_user().await.unwrap(); + + { + // user cannot be found after deletion + let search_result = admin_client + .admin_list_users(Some(&user.email)) + .await + .unwrap(); + let target_user = search_result.into_iter().find(|u| u.email == user.email); + assert!(target_user.is_none(), "User should be deleted: {:?}", user); + } +} + #[tokio::test] async fn admin_delete_create_same_user_hard() { let (client, user) = generate_unique_registered_user_client().await;