Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- Add down migration script here
2 changes: 2 additions & 0 deletions identity/migrations/20231108065148_user_deleted_at.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Add up migration script here
ALTER TABLE "user" ADD "deleted_at" TIMESTAMPTZ DEFAULT NULL;
15 changes: 15 additions & 0 deletions identity/src/entities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct User {
pub date_of_birth: Option<chrono::DateTime<chrono::Utc>>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub deleted_at: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)]
Expand All @@ -36,6 +37,20 @@ pub struct Ministry {
pub updated_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)]
pub struct UserConnectGroup {
pub user_id: String,
pub connect_group_id: Option<String>,
pub user_role: String,
}

#[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)]
pub struct UserMinistry {
pub user_id: String,
pub ministry_id: Option<String>,
pub user_role: String,
}

#[derive(Debug, Clone, Deserialize, Serialize, Object, sqlx::FromRow)]
pub struct PastoralRole {
pub id: String,
Expand Down
19 changes: 18 additions & 1 deletion identity/src/routes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<users::list_deleted_users::Response, users::list_deleted_users::Error> {
self._list_deleted_users(db).await
}

/// Get a user
///
/// Retrieve a user's details given its id.
Expand Down Expand Up @@ -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",
Expand Down
22 changes: 21 additions & 1 deletion identity/src/routes/users/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ 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
)
Expand All @@ -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)))
}
}
6 changes: 6 additions & 0 deletions identity/src/routes/users/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
}
}
1 change: 1 addition & 0 deletions identity/src/routes/users/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ impl crate::routes::Routes {
entities::User,
r#"
SELECT * from "user"
WHERE deleted_at IS NULL
"#,
)
.fetch_all(&db.db)
Expand Down
39 changes: 39 additions & 0 deletions identity/src/routes/users/list_deleted_users.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<entities::User>>),
}

#[derive(poem_openapi::ApiResponse)]
pub enum Error {
#[oai(status = 400)]
BadRequest(payload::Json<ErrorResponse>),

#[oai(status = 500)]
InternalServer(payload::Json<ErrorResponse>),
}

impl crate::routes::Routes {
pub async fn _list_deleted_users(&self, db: web::Data<&Database>) -> Result<Response, Error> {
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)))
}
}
1 change: 1 addition & 0 deletions identity/src/routes/users/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;