From ebb9f2b780ba85340a57fedf237426a6f7d37b31 Mon Sep 17 00:00:00 2001 From: Alan Date: Sat, 28 Oct 2023 15:27:06 +0800 Subject: [PATCH 1/8] Added get_deleted_users --- .../migrations/20230731173925_schema.up.sql | 1 + identity/src/entities/mod.rs | 1 + identity/src/routes/mod.rs | 18 +++++++++ .../src/routes/users/get_deleted_users.rs | 39 +++++++++++++++++++ identity/src/routes/users/mod.rs | 1 + 5 files changed, 60 insertions(+) create mode 100644 identity/src/routes/users/get_deleted_users.rs diff --git a/identity/migrations/20230731173925_schema.up.sql b/identity/migrations/20230731173925_schema.up.sql index 4a9ffe5..2f6ffdd 100644 --- a/identity/migrations/20230731173925_schema.up.sql +++ b/identity/migrations/20230731173925_schema.up.sql @@ -131,6 +131,7 @@ CREATE TABLE "user" ( date_of_birth TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ DEFAULT NULL, PRIMARY KEY (id), UNIQUE (no), UNIQUE (email), diff --git a/identity/src/entities/mod.rs b/identity/src/entities/mod.rs index 9a4db8b..6860f91 100644 --- a/identity/src/entities/mod.rs +++ b/identity/src/entities/mod.rs @@ -22,6 +22,7 @@ pub struct User { pub date_of_birth: Option>, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, + pub deleted_at: chrono::DateTime, } #[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)] diff --git a/identity/src/routes/mod.rs b/identity/src/routes/mod.rs index 9e3c97f..cca1d1f 100644 --- a/identity/src/routes/mod.rs +++ b/identity/src/routes/mod.rs @@ -144,6 +144,24 @@ impl Routes { self._delete_user(db, id).await } + /// List or search deleted users + /// + /// Retrieve a list of deleted users or search for deleted users given a query. For now it only supports + /// retrieving a list of deleted users. + #[oai( + path = "/users", + method = "get", + operation_id = "get_deleted_users", + tag = "Tag::User" + )] + async fn get_deleted_users( + &self, + _auth: BearerAuth, + db: web::Data<&Database>, + ) -> Result { + self._get_deleted_users(db).await + } + /// Get a user's pastoral roles /// /// List the pastoral roles associated with a user. diff --git a/identity/src/routes/users/get_deleted_users.rs b/identity/src/routes/users/get_deleted_users.rs new file mode 100644 index 0000000..903b6ca --- /dev/null +++ b/identity/src/routes/users/get_deleted_users.rs @@ -0,0 +1,39 @@ +use poem::web; +use poem_openapi::payload; + +use crate::{database::Database, entities, error::ErrorResponse}; + +#[derive(poem_openapi::ApiResponse)] +pub enum Response { + #[oai(status = 200)] + Ok(payload::Json>), +} + +#[derive(poem_openapi::ApiResponse)] +pub enum Error { + #[oai(status = 400)] + BadRequest(payload::Json), + + #[oai(status = 500)] + InternalServer(payload::Json), +} + +impl crate::routes::Routes { + pub async fn _get_deleted_users(&self, db: web::Data<&Database>) -> Result { + let users = sqlx::query_as_unchecked!( + entities::User, + r#" + SELECT * from "user" WHERE "deleted_at" != NULL + "#, + ) + .fetch_all(&db.db) + .await + .map_err(|e| match e { + _ => Error::InternalServer(payload::Json(ErrorResponse::from( + &e as &(dyn std::error::Error + Send + Sync), + ))), + })?; + + Ok(Response::Ok(payload::Json(users))) + } +} diff --git a/identity/src/routes/users/mod.rs b/identity/src/routes/users/mod.rs index f54ac83..db934e8 100644 --- a/identity/src/routes/users/mod.rs +++ b/identity/src/routes/users/mod.rs @@ -5,5 +5,6 @@ pub mod get_connect_groups; pub mod get_ministries; pub mod get_ministry_roles; pub mod get_pastoral_roles; +pub mod get_deleted_users; pub mod list; pub mod update; From 45516db4158e86e683432d28dab5c66ab198e3b0 Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 8 Nov 2023 13:36:20 +0800 Subject: [PATCH 2/8] Revert "Added get_deleted_users" This reverts commit ebb9f2b780ba85340a57fedf237426a6f7d37b31. --- .../migrations/20230731173925_schema.up.sql | 1 - identity/src/entities/mod.rs | 1 - identity/src/routes/mod.rs | 18 --------- .../src/routes/users/get_deleted_users.rs | 39 ------------------- identity/src/routes/users/mod.rs | 1 - 5 files changed, 60 deletions(-) delete mode 100644 identity/src/routes/users/get_deleted_users.rs diff --git a/identity/migrations/20230731173925_schema.up.sql b/identity/migrations/20230731173925_schema.up.sql index 2f6ffdd..4a9ffe5 100644 --- a/identity/migrations/20230731173925_schema.up.sql +++ b/identity/migrations/20230731173925_schema.up.sql @@ -131,7 +131,6 @@ CREATE TABLE "user" ( date_of_birth TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - deleted_at TIMESTAMPTZ DEFAULT NULL, PRIMARY KEY (id), UNIQUE (no), UNIQUE (email), diff --git a/identity/src/entities/mod.rs b/identity/src/entities/mod.rs index 6860f91..9a4db8b 100644 --- a/identity/src/entities/mod.rs +++ b/identity/src/entities/mod.rs @@ -22,7 +22,6 @@ pub struct User { pub date_of_birth: Option>, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, - pub deleted_at: chrono::DateTime, } #[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)] diff --git a/identity/src/routes/mod.rs b/identity/src/routes/mod.rs index cca1d1f..9e3c97f 100644 --- a/identity/src/routes/mod.rs +++ b/identity/src/routes/mod.rs @@ -144,24 +144,6 @@ impl Routes { self._delete_user(db, id).await } - /// List or search deleted users - /// - /// Retrieve a list of deleted users or search for deleted users given a query. For now it only supports - /// retrieving a list of deleted users. - #[oai( - path = "/users", - method = "get", - operation_id = "get_deleted_users", - tag = "Tag::User" - )] - async fn get_deleted_users( - &self, - _auth: BearerAuth, - db: web::Data<&Database>, - ) -> Result { - self._get_deleted_users(db).await - } - /// Get a user's pastoral roles /// /// List the pastoral roles associated with a user. diff --git a/identity/src/routes/users/get_deleted_users.rs b/identity/src/routes/users/get_deleted_users.rs deleted file mode 100644 index 903b6ca..0000000 --- a/identity/src/routes/users/get_deleted_users.rs +++ /dev/null @@ -1,39 +0,0 @@ -use poem::web; -use poem_openapi::payload; - -use crate::{database::Database, entities, error::ErrorResponse}; - -#[derive(poem_openapi::ApiResponse)] -pub enum Response { - #[oai(status = 200)] - Ok(payload::Json>), -} - -#[derive(poem_openapi::ApiResponse)] -pub enum Error { - #[oai(status = 400)] - BadRequest(payload::Json), - - #[oai(status = 500)] - InternalServer(payload::Json), -} - -impl crate::routes::Routes { - pub async fn _get_deleted_users(&self, db: web::Data<&Database>) -> Result { - let users = sqlx::query_as_unchecked!( - entities::User, - r#" - SELECT * from "user" WHERE "deleted_at" != NULL - "#, - ) - .fetch_all(&db.db) - .await - .map_err(|e| match e { - _ => Error::InternalServer(payload::Json(ErrorResponse::from( - &e as &(dyn std::error::Error + Send + Sync), - ))), - })?; - - Ok(Response::Ok(payload::Json(users))) - } -} diff --git a/identity/src/routes/users/mod.rs b/identity/src/routes/users/mod.rs index db934e8..f54ac83 100644 --- a/identity/src/routes/users/mod.rs +++ b/identity/src/routes/users/mod.rs @@ -5,6 +5,5 @@ pub mod get_connect_groups; pub mod get_ministries; pub mod get_ministry_roles; pub mod get_pastoral_roles; -pub mod get_deleted_users; pub mod list; pub mod update; From b57cdeee52ebbc5eb83d31f543868014891abac0 Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 23 Nov 2023 17:04:03 +0800 Subject: [PATCH 3/8] Added deleted_at column to user table --- .../20231108065148_user_deleted_at.down.sql | 1 + .../20231108065148_user_deleted_at.up.sql | 2 ++ identity/src/entities/mod.rs | 15 +++++++++++++++ 3 files changed, 18 insertions(+) create mode 100644 identity/migrations/20231108065148_user_deleted_at.down.sql create mode 100644 identity/migrations/20231108065148_user_deleted_at.up.sql diff --git a/identity/migrations/20231108065148_user_deleted_at.down.sql b/identity/migrations/20231108065148_user_deleted_at.down.sql new file mode 100644 index 0000000..d2f607c --- /dev/null +++ b/identity/migrations/20231108065148_user_deleted_at.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/identity/migrations/20231108065148_user_deleted_at.up.sql b/identity/migrations/20231108065148_user_deleted_at.up.sql new file mode 100644 index 0000000..3a334e6 --- /dev/null +++ b/identity/migrations/20231108065148_user_deleted_at.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE "user" ADD "deleted_at" TIMESTAMPTZ DEFAULT NULL; \ No newline at end of file diff --git a/identity/src/entities/mod.rs b/identity/src/entities/mod.rs index 9a4db8b..c128270 100644 --- a/identity/src/entities/mod.rs +++ b/identity/src/entities/mod.rs @@ -22,6 +22,7 @@ pub struct User { pub date_of_birth: Option>, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, + pub deleted_at : chrono::DateTime, } #[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)] @@ -36,6 +37,20 @@ pub struct Ministry { pub updated_at: chrono::DateTime, } +#[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)] +pub struct UserConnectGroup { + pub user_id: String, + pub connect_group_id: Option, + pub user_role: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)] +pub struct UserMinistry { + pub user_id: String, + pub ministry_id: Option, + pub user_role: String, +} + #[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)] pub struct PastoralRole { pub id: String, From df270b94bb58fa40bd1c96f903b3f0cabb07613c Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 23 Nov 2023 17:04:58 +0800 Subject: [PATCH 4/8] Soft delete users --- identity/src/routes/users/delete.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/identity/src/routes/users/delete.rs b/identity/src/routes/users/delete.rs index dfca2f9..f2bfba0 100644 --- a/identity/src/routes/users/delete.rs +++ b/identity/src/routes/users/delete.rs @@ -30,11 +30,11 @@ impl crate::routes::Routes { let user = sqlx::query_as_unchecked!( entities::User, r#" - DELETE FROM "user" WHERE id = $1::TEXT RETURNING * + UPDATE "user" SET "deleted_at" = NOW() WHERE id = $1::TEXT RETURNING * "#, - &*id + &*id ) - .fetch_one(&db.db) + .fetch_one(&db.db) .await .map_err(|e| match e { sqlx::error::Error::RowNotFound => Error::NotFound(payload::Json(ErrorResponse::from( @@ -45,6 +45,26 @@ impl crate::routes::Routes { ))), })?; + let _user_cg = sqlx::query_as_unchecked!( + entities::UserConnectGroup, + r#" + DELETE FROM "user_connect_group" WHERE user_id = $1::TEXT RETURNING * + "#, + &*id + ) + .fetch_one(&db.db) + .await; + + let _user_ministry = sqlx::query_as_unchecked!( + entities::UserMinistry, + r#" + DELETE FROM "user_ministry" WHERE user_id = $1::TEXT RETURNING * + "#, + &*id + ) + .fetch_one(&db.db) + .await; + Ok(Response::Ok(payload::Json(user))) } } From edcfa97ee85ff92593ea3ae2c360716a72bf6b8f Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 23 Nov 2023 17:05:18 +0800 Subject: [PATCH 5/8] list_deleted_users api --- identity/src/routes/mod.rs | 19 ++++++++- .../src/routes/users/list_deleted_users.rs | 39 +++++++++++++++++++ identity/src/routes/users/mod.rs | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 identity/src/routes/users/list_deleted_users.rs diff --git a/identity/src/routes/mod.rs b/identity/src/routes/mod.rs index 9e3c97f..0479e7c 100644 --- a/identity/src/routes/mod.rs +++ b/identity/src/routes/mod.rs @@ -89,6 +89,23 @@ impl Routes { self._list_users(db).await } + /// List or search soft deleted users + /// + /// Retrieve a list of soft deleted users or search for deleted users given a query. + #[oai( + path = "/deleted-users", + method = "get", + operation_id = "list-deleted-users", + tag = "Tag::User" + )] + async fn list_deleted_users( + &self, + _auth: BearerAuth, + db: web::Data<&Database>, + ) -> Result { + self._list_deleted_users(db).await + } + /// Get a user /// /// Retrieve a user's details given its id. @@ -128,7 +145,7 @@ impl Routes { /// Delete a user /// - /// Deletes a user based on the id from the database. + /// Soft deletes a user based on the id from the database. The associated cg and ministry of the user will be deleted. #[oai( path = "/users/:id", method = "delete", diff --git a/identity/src/routes/users/list_deleted_users.rs b/identity/src/routes/users/list_deleted_users.rs new file mode 100644 index 0000000..7dff596 --- /dev/null +++ b/identity/src/routes/users/list_deleted_users.rs @@ -0,0 +1,39 @@ +use poem::web; +use poem_openapi::payload; + +use crate::{database::Database, entities, error::ErrorResponse}; + +#[derive(poem_openapi::ApiResponse)] +pub enum Response { + #[oai(status = 200)] + Ok(payload::Json>), +} + +#[derive(poem_openapi::ApiResponse)] +pub enum Error { + #[oai(status = 400)] + BadRequest(payload::Json), + + #[oai(status = 500)] + InternalServer(payload::Json), +} + +impl crate::routes::Routes { + pub async fn _list_deleted_users(&self, db: web::Data<&Database>) -> Result { + let users = sqlx::query_as_unchecked!( + entities::User, + r#" + SELECT * from "user" WHERE deleted_at IS NOT NULL + "#, + ) + .fetch_all(&db.db) + .await + .map_err(|e| match e { + _ => Error::InternalServer(payload::Json(ErrorResponse::from( + &e as &(dyn std::error::Error + Send + Sync), + ))), + })?; + + Ok(Response::Ok(payload::Json(users))) + } +} diff --git a/identity/src/routes/users/mod.rs b/identity/src/routes/users/mod.rs index f54ac83..8a47efd 100644 --- a/identity/src/routes/users/mod.rs +++ b/identity/src/routes/users/mod.rs @@ -6,4 +6,5 @@ pub mod get_ministries; pub mod get_ministry_roles; pub mod get_pastoral_roles; pub mod list; +pub mod list_deleted_users; pub mod update; From 9956d0a916854199590be2f46dad4d43aaa461f1 Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 23 Nov 2023 17:23:27 +0800 Subject: [PATCH 6/8] cargo fmt --- identity/src/entities/mod.rs | 2 +- identity/src/routes/mod.rs | 2 +- identity/src/routes/users/delete.rs | 32 ++++++++++++++--------------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/identity/src/entities/mod.rs b/identity/src/entities/mod.rs index c128270..2dd4837 100644 --- a/identity/src/entities/mod.rs +++ b/identity/src/entities/mod.rs @@ -22,7 +22,7 @@ pub struct User { pub date_of_birth: Option>, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, - pub deleted_at : chrono::DateTime, + pub deleted_at: chrono::DateTime, } #[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)] diff --git a/identity/src/routes/mod.rs b/identity/src/routes/mod.rs index 0479e7c..dd043f2 100644 --- a/identity/src/routes/mod.rs +++ b/identity/src/routes/mod.rs @@ -89,7 +89,7 @@ impl Routes { self._list_users(db).await } - /// List or search soft deleted users + /// List or search soft deleted users /// /// Retrieve a list of soft deleted users or search for deleted users given a query. #[oai( diff --git a/identity/src/routes/users/delete.rs b/identity/src/routes/users/delete.rs index f2bfba0..c913559 100644 --- a/identity/src/routes/users/delete.rs +++ b/identity/src/routes/users/delete.rs @@ -32,9 +32,9 @@ impl crate::routes::Routes { r#" UPDATE "user" SET "deleted_at" = NOW() WHERE id = $1::TEXT RETURNING * "#, - &*id + &*id ) - .fetch_one(&db.db) + .fetch_one(&db.db) .await .map_err(|e| match e { sqlx::error::Error::RowNotFound => Error::NotFound(payload::Json(ErrorResponse::from( @@ -45,25 +45,25 @@ impl crate::routes::Routes { ))), })?; - let _user_cg = sqlx::query_as_unchecked!( - entities::UserConnectGroup, - r#" + let _user_cg = sqlx::query_as_unchecked!( + entities::UserConnectGroup, + r#" DELETE FROM "user_connect_group" WHERE user_id = $1::TEXT RETURNING * "#, - &*id - ) - .fetch_one(&db.db) - .await; + &*id + ) + .fetch_one(&db.db) + .await; - let _user_ministry = sqlx::query_as_unchecked!( - entities::UserMinistry, - r#" + let _user_ministry = sqlx::query_as_unchecked!( + entities::UserMinistry, + r#" DELETE FROM "user_ministry" WHERE user_id = $1::TEXT RETURNING * "#, - &*id - ) - .fetch_one(&db.db) - .await; + &*id + ) + .fetch_one(&db.db) + .await; Ok(Response::Ok(payload::Json(user))) } From a0bd215442c5cbc3b29c3a276cc4681eb8dadffe Mon Sep 17 00:00:00 2001 From: Jun Jie Date: Mon, 20 Nov 2023 19:25:44 +0800 Subject: [PATCH 7/8] Updated get user to return error if user is soft deleted Updated get all user to not return soft deleted user --- identity/src/routes/users/get.rs | 6 ++++++ identity/src/routes/users/list.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/identity/src/routes/users/get.rs b/identity/src/routes/users/get.rs index 375efd3..0c1b30f 100644 --- a/identity/src/routes/users/get.rs +++ b/identity/src/routes/users/get.rs @@ -45,6 +45,12 @@ impl crate::routes::Routes { ))), })?; + if user.deleted_at.is_some() { + return Err(Error::BadRequest(payload::Json(ErrorResponse { + message: format!("User with id '{}' is deleted", &*id), + }))); + } + Ok(Response::Ok(payload::Json(user))) } } diff --git a/identity/src/routes/users/list.rs b/identity/src/routes/users/list.rs index 368d9ca..0d0b045 100644 --- a/identity/src/routes/users/list.rs +++ b/identity/src/routes/users/list.rs @@ -24,6 +24,7 @@ impl crate::routes::Routes { entities::User, r#" SELECT * from "user" + WHERE deleted_at IS NULL "#, ) .fetch_all(&db.db) From ba587cc5ef8bc4913a7531d2720be0d8629b5a62 Mon Sep 17 00:00:00 2001 From: Jun Jie Date: Mon, 27 Nov 2023 21:36:32 +0800 Subject: [PATCH 8/8] Update deleted_at to become optional --- identity/src/entities/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity/src/entities/mod.rs b/identity/src/entities/mod.rs index 2dd4837..e4c797b 100644 --- a/identity/src/entities/mod.rs +++ b/identity/src/entities/mod.rs @@ -22,7 +22,7 @@ pub struct User { pub date_of_birth: Option>, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, - pub deleted_at: chrono::DateTime, + pub deleted_at: Option>, } #[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)]