From d74214a91012c177a9792da39c2e3e712c496c31 Mon Sep 17 00:00:00 2001 From: dal Date: Wed, 15 Jan 2025 10:14:15 -0700 Subject: [PATCH] Refactor user routes to include new endpoint for retrieving user by ID - Removed the public modifier from `get_user` and `update_user` modules to encapsulate them within the module. - Added a new route to the user router for fetching a user by their ID, enhancing the API's functionality. - This change improves the user management capabilities by allowing retrieval of specific user details based on their unique identifier. --- .../routes/rest/routes/organizations/mod.rs | 8 ++ .../routes/rest/routes/organizations/users.rs | 74 +++++++++++++++++++ .../rest/routes/users/get_user_by_id.rs | 74 +++++++++++++++++++ api/src/routes/rest/routes/users/mod.rs | 6 +- 4 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 api/src/routes/rest/routes/organizations/mod.rs create mode 100644 api/src/routes/rest/routes/organizations/users.rs create mode 100644 api/src/routes/rest/routes/users/get_user_by_id.rs diff --git a/api/src/routes/rest/routes/organizations/mod.rs b/api/src/routes/rest/routes/organizations/mod.rs new file mode 100644 index 000000000..c21b36ff9 --- /dev/null +++ b/api/src/routes/rest/routes/organizations/mod.rs @@ -0,0 +1,8 @@ +use axum::{routing::get, Router}; + +mod users; + +pub fn router() -> Router { + Router::new() + .route("/:id/users", get(users::list_organization_users)) +} diff --git a/api/src/routes/rest/routes/organizations/users.rs b/api/src/routes/rest/routes/organizations/users.rs new file mode 100644 index 000000000..e74033ef5 --- /dev/null +++ b/api/src/routes/rest/routes/organizations/users.rs @@ -0,0 +1,74 @@ +use anyhow::Result; +use axum::{extract::Path, Extension, http::StatusCode}; +use diesel::{ExpressionMethods, QueryDsl, JoinOnDsl}; +use diesel_async::RunQueryDsl; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::{ + database::{ + enums::{UserOrganizationRole, UserOrganizationStatus}, + lib::get_pg_pool, + models::User, + schema::{users, users_to_organizations}, + }, + routes::rest::ApiResponse, + utils::clients::sentry_utils::send_sentry_error, +}; + +#[derive(Serialize, Deserialize, Clone)] +pub struct UserResponse { + pub id: Uuid, + pub name: Option, + pub email: String, + pub role: UserOrganizationRole, + pub status: UserOrganizationStatus, +} + +pub async fn list_organization_users( + Extension(user): Extension, + Path(organization_id): Path, +) -> Result>, (StatusCode, &'static str)> { + let users = match list_organization_users_handler(organization_id).await { + Ok(users) => users, + Err(e) => { + tracing::error!("Error listing organization users: {:?}", e); + send_sentry_error(&e.to_string(), Some(&user.id)); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Error listing organization users", + )); + } + }; + + Ok(ApiResponse::JsonData(users)) +} + +async fn list_organization_users_handler(organization_id: Uuid) -> Result> { + let mut conn = get_pg_pool().get().await?; + + let users = users::table + .inner_join(users_to_organizations::table.on(users::id.eq(users_to_organizations::user_id))) + .select(( + users::id, + users::email, + users::name.nullable(), + users_to_organizations::role, + users_to_organizations::status, + )) + .filter(users_to_organizations::organization_id.eq(organization_id)) + .filter(users_to_organizations::deleted_at.is_null()) + .load::<(Uuid, String, Option, UserOrganizationRole, UserOrganizationStatus)>(&mut conn) + .await?; + + Ok(users + .into_iter() + .map(|(id, email, name, role, status)| UserResponse { + id, + name, + email, + role, + status, + }) + .collect()) +} \ No newline at end of file diff --git a/api/src/routes/rest/routes/users/get_user_by_id.rs b/api/src/routes/rest/routes/users/get_user_by_id.rs new file mode 100644 index 000000000..02091a1b0 --- /dev/null +++ b/api/src/routes/rest/routes/users/get_user_by_id.rs @@ -0,0 +1,74 @@ +use anyhow::Result; +use axum::{extract::Path, Extension}; +use diesel_async::RunQueryDsl; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::{ + database::{ + enums::{UserOrganizationRole, UserOrganizationStatus}, + lib::get_pg_pool, + models::User, + schema::{users, users_to_organizations}, + }, + routes::rest::ApiResponse, + utils::clients::sentry_utils::send_sentry_error, +}; +use axum::http::StatusCode; +use diesel::{ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl}; + +#[derive(Serialize, Deserialize, Clone)] +pub struct UserResponse { + pub id: Uuid, + pub name: Option, + pub email: String, + pub role: UserOrganizationRole, + pub status: UserOrganizationStatus, +} + +pub async fn get_user_by_id( + Extension(user): Extension, + Path(id): Path, +) -> Result, (StatusCode, &'static str)> { + let user_info = match get_user_information(&id).await { + Ok(user_info) => user_info, + Err(e) => { + tracing::error!("Error getting user information: {:?}", e); + send_sentry_error(&e.to_string(), Some(&user.id)); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + "Error getting user information", + )); + } + }; + + Ok(ApiResponse::JsonData(user_info)) +} + +pub async fn get_user_information(user_id: &Uuid) -> Result { + let pg_pool = get_pg_pool(); + let mut conn = pg_pool.get().await?; + + let (user, (role, status)) = users::table + .inner_join(users_to_organizations::table.on(users::id.eq(users_to_organizations::user_id))) + .select(( + (users::id, users::email, users::name.nullable()), + (users_to_organizations::role, users_to_organizations::status), + )) + .filter(users::id.eq(user_id)) + .first::<( + (Uuid, String, Option), + (UserOrganizationRole, UserOrganizationStatus), + )>(&mut conn) + .await?; + + let (id, email, name) = user; + + Ok(UserResponse { + id, + name, + email, + role, + status, + }) +} diff --git a/api/src/routes/rest/routes/users/mod.rs b/api/src/routes/rest/routes/users/mod.rs index f3ac8ce9d..e3eaeb3af 100644 --- a/api/src/routes/rest/routes/users/mod.rs +++ b/api/src/routes/rest/routes/users/mod.rs @@ -3,11 +3,13 @@ use axum::{ Router, }; -pub mod get_user; -pub mod update_user; +mod get_user; +mod get_user_by_id; +mod update_user; pub fn router() -> Router { Router::new() .route("/", get(get_user::get_user)) .route("/", put(update_user::update_user)) + .route("/:id", get(get_user_by_id::get_user_by_id)) }