mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +08:00 
			
		
		
		
	feat(users): Signin and Verify Email changes for User Invitation changes (#3420)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
		| @ -58,6 +58,8 @@ pub enum UserErrors { | ||||
|     InvalidDeleteOperation, | ||||
|     #[error("MaxInvitationsError")] | ||||
|     MaxInvitationsError, | ||||
|     #[error("RoleNotFound")] | ||||
|     RoleNotFound, | ||||
| } | ||||
|  | ||||
| impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorResponse> for UserErrors { | ||||
| @ -146,6 +148,9 @@ impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorRespon | ||||
|             Self::MaxInvitationsError => { | ||||
|                 AER::BadRequest(ApiError::new(sub_code, 31, self.get_error_message(), None)) | ||||
|             } | ||||
|             Self::RoleNotFound => { | ||||
|                 AER::BadRequest(ApiError::new(sub_code, 32, self.get_error_message(), None)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -178,6 +183,7 @@ impl UserErrors { | ||||
|             Self::ChangePasswordError => "Old and new password cannot be the same", | ||||
|             Self::InvalidDeleteOperation => "Delete Operation Not Supported", | ||||
|             Self::MaxInvitationsError => "Maximum invite count per request exceeded", | ||||
|             Self::RoleNotFound => "Role Not Found", | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -97,10 +97,10 @@ pub async fn signup( | ||||
|     )) | ||||
| } | ||||
|  | ||||
| pub async fn signin( | ||||
| pub async fn signin_without_invite_checks( | ||||
|     state: AppState, | ||||
|     request: user_api::SignInRequest, | ||||
| ) -> UserResponse<user_api::SignInResponse> { | ||||
| ) -> UserResponse<user_api::DashboardEntryResponse> { | ||||
|     let user_from_db: domain::UserFromStorage = state | ||||
|         .store | ||||
|         .find_user_by_email(request.email.clone().expose().expose().as_str()) | ||||
| @ -124,6 +124,50 @@ pub async fn signin( | ||||
|     )) | ||||
| } | ||||
|  | ||||
| pub async fn signin( | ||||
|     state: AppState, | ||||
|     request: user_api::SignInRequest, | ||||
| ) -> UserResponse<user_api::SignInResponse> { | ||||
|     let user_from_db: domain::UserFromStorage = state | ||||
|         .store | ||||
|         .find_user_by_email(request.email.clone().expose().expose().as_str()) | ||||
|         .await | ||||
|         .map_err(|e| { | ||||
|             if e.current_context().is_db_not_found() { | ||||
|                 e.change_context(UserErrors::InvalidCredentials) | ||||
|             } else { | ||||
|                 e.change_context(UserErrors::InternalServerError) | ||||
|             } | ||||
|         })? | ||||
|         .into(); | ||||
|  | ||||
|     user_from_db.compare_password(request.password)?; | ||||
|  | ||||
|     let signin_strategy = | ||||
|         if let Some(preferred_merchant_id) = user_from_db.get_preferred_merchant_id() { | ||||
|             let preferred_role = user_from_db | ||||
|                 .get_role_from_db_by_merchant_id(&state, preferred_merchant_id.as_str()) | ||||
|                 .await | ||||
|                 .change_context(UserErrors::InternalServerError) | ||||
|                 .attach_printable("User role with preferred_merchant_id not found")?; | ||||
|             domain::SignInWithRoleStrategyType::SingleRole(domain::SignInWithSingleRoleStrategy { | ||||
|                 user: user_from_db, | ||||
|                 user_role: preferred_role, | ||||
|             }) | ||||
|         } else { | ||||
|             let user_roles = user_from_db.get_roles_from_db(&state).await?; | ||||
|             domain::SignInWithRoleStrategyType::decide_signin_strategy_by_user_roles( | ||||
|                 user_from_db, | ||||
|                 user_roles, | ||||
|             ) | ||||
|             .await? | ||||
|         }; | ||||
|  | ||||
|     Ok(ApplicationResponse::Json( | ||||
|         signin_strategy.get_signin_response(&state).await?, | ||||
|     )) | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "email")] | ||||
| pub async fn connect_account( | ||||
|     state: AppState, | ||||
| @ -832,22 +876,22 @@ pub async fn list_merchant_ids_for_user( | ||||
|     state: AppState, | ||||
|     user: auth::UserFromToken, | ||||
| ) -> UserResponse<Vec<user_api::UserMerchantAccount>> { | ||||
|     let merchant_ids = utils::user_role::get_merchant_ids_for_user(&state, &user.user_id).await?; | ||||
|     let user_roles = | ||||
|         utils::user_role::get_active_user_roles_for_user(&state, &user.user_id).await?; | ||||
|  | ||||
|     let merchant_accounts = state | ||||
|         .store | ||||
|         .list_multiple_merchant_accounts(merchant_ids) | ||||
|         .list_multiple_merchant_accounts( | ||||
|             user_roles | ||||
|                 .iter() | ||||
|                 .map(|role| role.merchant_id.clone()) | ||||
|                 .collect(), | ||||
|         ) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
|     Ok(ApplicationResponse::Json( | ||||
|         merchant_accounts | ||||
|             .into_iter() | ||||
|             .map(|acc| user_api::UserMerchantAccount { | ||||
|                 merchant_id: acc.merchant_id, | ||||
|                 merchant_name: acc.merchant_name, | ||||
|             }) | ||||
|             .collect(), | ||||
|         utils::user::get_multiple_merchant_details_with_status(user_roles, merchant_accounts)?, | ||||
|     )) | ||||
| } | ||||
|  | ||||
| @ -868,11 +912,38 @@ pub async fn get_users_for_merchant_account( | ||||
|     Ok(ApplicationResponse::Json(user_api::GetUsersResponse(users))) | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "email")] | ||||
| pub async fn verify_email_without_invite_checks( | ||||
|     state: AppState, | ||||
|     req: user_api::VerifyEmailRequest, | ||||
| ) -> UserResponse<user_api::DashboardEntryResponse> { | ||||
|     let token = auth::decode_jwt::<email_types::EmailToken>(&req.token.clone().expose(), &state) | ||||
|         .await | ||||
|         .change_context(UserErrors::LinkInvalid)?; | ||||
|     let user = state | ||||
|         .store | ||||
|         .find_user_by_email(token.get_email()) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|     let user = state | ||||
|         .store | ||||
|         .update_user_by_user_id(user.user_id.as_str(), storage_user::UserUpdate::VerifyUser) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|     let user_from_db: domain::UserFromStorage = user.into(); | ||||
|     let user_role = user_from_db.get_role_from_db(state.clone()).await?; | ||||
|     let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?; | ||||
|  | ||||
|     Ok(ApplicationResponse::Json( | ||||
|         utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token)?, | ||||
|     )) | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "email")] | ||||
| pub async fn verify_email( | ||||
|     state: AppState, | ||||
|     req: user_api::VerifyEmailRequest, | ||||
| ) -> UserResponse<user_api::VerifyEmailResponse> { | ||||
| ) -> UserResponse<user_api::SignInResponse> { | ||||
|     let token = auth::decode_jwt::<email_types::EmailToken>(&req.token.clone().expose(), &state) | ||||
|         .await | ||||
|         .change_context(UserErrors::LinkInvalid)?; | ||||
| @ -890,11 +961,29 @@ pub async fn verify_email( | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
|     let user_from_db: domain::UserFromStorage = user.into(); | ||||
|     let user_role = user_from_db.get_role_from_db(state.clone()).await?; | ||||
|     let token = utils::user::generate_jwt_auth_token(&state, &user_from_db, &user_role).await?; | ||||
|  | ||||
|     let signin_strategy = | ||||
|         if let Some(preferred_merchant_id) = user_from_db.get_preferred_merchant_id() { | ||||
|             let preferred_role = user_from_db | ||||
|                 .get_role_from_db_by_merchant_id(&state, preferred_merchant_id.as_str()) | ||||
|                 .await | ||||
|                 .change_context(UserErrors::InternalServerError) | ||||
|                 .attach_printable("User role with preferred_merchant_id not found")?; | ||||
|             domain::SignInWithRoleStrategyType::SingleRole(domain::SignInWithSingleRoleStrategy { | ||||
|                 user: user_from_db, | ||||
|                 user_role: preferred_role, | ||||
|             }) | ||||
|         } else { | ||||
|             let user_roles = user_from_db.get_roles_from_db(&state).await?; | ||||
|             domain::SignInWithRoleStrategyType::decide_signin_strategy_by_user_roles( | ||||
|                 user_from_db, | ||||
|                 user_roles, | ||||
|             ) | ||||
|             .await? | ||||
|         }; | ||||
|  | ||||
|     Ok(ApplicationResponse::Json( | ||||
|         utils::user::get_dashboard_entry_response(&state, user_from_db, user_role, token)?, | ||||
|         signin_strategy.get_signin_response(&state).await?, | ||||
|     )) | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -952,7 +952,10 @@ impl User { | ||||
|         let mut route = web::scope("/user").app_data(web::Data::new(state)); | ||||
|  | ||||
|         route = route | ||||
|             .service(web::resource("/signin").route(web::post().to(user_signin))) | ||||
|             .service( | ||||
|                 web::resource("/signin").route(web::post().to(user_signin_without_invite_checks)), | ||||
|             ) | ||||
|             .service(web::resource("/v2/signin").route(web::post().to(user_signin))) | ||||
|             .service(web::resource("/change_password").route(web::post().to(change_password))) | ||||
|             .service(web::resource("/internal_signup").route(web::post().to(internal_user_signup))) | ||||
|             .service(web::resource("/switch_merchant").route(web::post().to(switch_merchant_id))) | ||||
| @ -961,14 +964,7 @@ impl User { | ||||
|                     .route(web::post().to(user_merchant_account_create)), | ||||
|             ) | ||||
|             .service(web::resource("/switch/list").route(web::get().to(list_merchant_ids_for_user))) | ||||
|             .service(web::resource("/user/list").route(web::get().to(get_user_details))) | ||||
|             .service(web::resource("/permission_info").route(web::get().to(get_authorization_info))) | ||||
|             .service(web::resource("/user/update_role").route(web::post().to(update_user_role))) | ||||
|             .service(web::resource("/role/list").route(web::get().to(list_roles))) | ||||
|             .service(web::resource("/role").route(web::get().to(get_role_from_token))) | ||||
|             .service(web::resource("/role/{role_id}").route(web::get().to(get_role))) | ||||
|             .service(web::resource("/user/invite").route(web::post().to(invite_user))) | ||||
|             .service(web::resource("/user/invite/accept").route(web::post().to(accept_invitation))) | ||||
|             .service(web::resource("/update").route(web::post().to(update_user_account_details))) | ||||
|             .service( | ||||
|                 web::resource("/user/invite_multiple").route(web::post().to(invite_multiple_user)), | ||||
| @ -980,6 +976,23 @@ impl User { | ||||
|             ) | ||||
|             .service(web::resource("/user/delete").route(web::delete().to(delete_user_role))); | ||||
|  | ||||
|         // User management | ||||
|         route = route.service( | ||||
|             web::scope("/user") | ||||
|                 .service(web::resource("/list").route(web::get().to(get_user_details))) | ||||
|                 .service(web::resource("/invite").route(web::post().to(invite_user))) | ||||
|                 .service(web::resource("/invite/accept").route(web::post().to(accept_invitation))) | ||||
|                 .service(web::resource("/update_role").route(web::post().to(update_user_role))), | ||||
|         ); | ||||
|  | ||||
|         // Role information | ||||
|         route = route.service( | ||||
|             web::scope("/role") | ||||
|                 .service(web::resource("").route(web::get().to(get_role_from_token))) | ||||
|                 .service(web::resource("/list").route(web::get().to(list_all_roles))) | ||||
|                 .service(web::resource("/{role_id}").route(web::get().to(get_role))), | ||||
|         ); | ||||
|  | ||||
|         #[cfg(feature = "dummy_connector")] | ||||
|         { | ||||
|             route = route.service( | ||||
| @ -1000,7 +1013,11 @@ impl User { | ||||
|                     web::resource("/signup_with_merchant_id") | ||||
|                         .route(web::post().to(user_signup_with_merchant_id)), | ||||
|                 ) | ||||
|                 .service(web::resource("/verify_email").route(web::post().to(verify_email))) | ||||
|                 .service( | ||||
|                     web::resource("/verify_email") | ||||
|                         .route(web::post().to(verify_email_without_invite_checks)), | ||||
|                 ) | ||||
|                 .service(web::resource("/v2/verify_email").route(web::post().to(verify_email))) | ||||
|                 .service( | ||||
|                     web::resource("/verify_email_request") | ||||
|                         .route(web::post().to(verify_email_request)), | ||||
|  | ||||
| @ -161,6 +161,7 @@ impl From<Flow> for ApiIdentifier { | ||||
|  | ||||
|             Flow::UserConnectAccount | ||||
|             | Flow::UserSignUp | ||||
|             | Flow::UserSignInWithoutInviteChecks | ||||
|             | Flow::UserSignIn | ||||
|             | Flow::ChangePassword | ||||
|             | Flow::SetDashboardMetadata | ||||
| @ -179,6 +180,7 @@ impl From<Flow> for ApiIdentifier { | ||||
|             | Flow::InviteMultipleUser | ||||
|             | Flow::DeleteUser | ||||
|             | Flow::UserSignUpWithMerchantId | ||||
|             | Flow::VerifyEmailWithoutInviteChecks | ||||
|             | Flow::VerifyEmail | ||||
|             | Flow::VerifyEmailRequest | ||||
|             | Flow::UpdateUserAccountDetails => Self::User, | ||||
|  | ||||
| @ -58,6 +58,25 @@ pub async fn user_signup( | ||||
|     .await | ||||
| } | ||||
|  | ||||
| pub async fn user_signin_without_invite_checks( | ||||
|     state: web::Data<AppState>, | ||||
|     http_req: HttpRequest, | ||||
|     json_payload: web::Json<user_api::SignInRequest>, | ||||
| ) -> HttpResponse { | ||||
|     let flow = Flow::UserSignInWithoutInviteChecks; | ||||
|     let req_payload = json_payload.into_inner(); | ||||
|     Box::pin(api::server_wrap( | ||||
|         flow.clone(), | ||||
|         state, | ||||
|         &http_req, | ||||
|         req_payload.clone(), | ||||
|         |state, _, req_body| user_core::signin_without_invite_checks(state, req_body), | ||||
|         &auth::NoAuth, | ||||
|         api_locking::LockAction::NotApplicable, | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| pub async fn user_signin( | ||||
|     state: web::Data<AppState>, | ||||
|     http_req: HttpRequest, | ||||
| @ -368,6 +387,25 @@ pub async fn invite_multiple_user( | ||||
|     .await | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "email")] | ||||
| pub async fn verify_email_without_invite_checks( | ||||
|     state: web::Data<AppState>, | ||||
|     http_req: HttpRequest, | ||||
|     json_payload: web::Json<user_api::VerifyEmailRequest>, | ||||
| ) -> HttpResponse { | ||||
|     let flow = Flow::VerifyEmailWithoutInviteChecks; | ||||
|     Box::pin(api::server_wrap( | ||||
|         flow.clone(), | ||||
|         state, | ||||
|         &http_req, | ||||
|         json_payload.into_inner(), | ||||
|         |state, _, req_payload| user_core::verify_email_without_invite_checks(state, req_payload), | ||||
|         &auth::NoAuth, | ||||
|         api_locking::LockAction::NotApplicable, | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "email")] | ||||
| pub async fn verify_email( | ||||
|     state: web::Data<AppState>, | ||||
|  | ||||
| @ -29,7 +29,7 @@ pub async fn get_authorization_info( | ||||
|     .await | ||||
| } | ||||
|  | ||||
| pub async fn list_roles(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse { | ||||
| pub async fn list_all_roles(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse { | ||||
|     let flow = Flow::ListRoles; | ||||
|     Box::pin(api::server_wrap( | ||||
|         flow, | ||||
|  | ||||
| @ -26,11 +26,12 @@ use crate::{ | ||||
|     db::StorageInterface, | ||||
|     routes::AppState, | ||||
|     services::{ | ||||
|         authentication as auth, | ||||
|         authentication::UserFromToken, | ||||
|         authorization::{info, predefined_permissions}, | ||||
|     }, | ||||
|     types::transformers::ForeignFrom, | ||||
|     utils::user::password, | ||||
|     utils::{self, user::password}, | ||||
| }; | ||||
|  | ||||
| pub mod dashboard_metadata; | ||||
| @ -733,7 +734,15 @@ impl UserFromStorage { | ||||
|     pub async fn get_role_from_db(&self, state: AppState) -> UserResult<UserRole> { | ||||
|         state | ||||
|             .store | ||||
|             .find_user_role_by_user_id(self.get_user_id()) | ||||
|             .find_user_role_by_user_id(&self.0.user_id) | ||||
|             .await | ||||
|             .change_context(UserErrors::InternalServerError) | ||||
|     } | ||||
|  | ||||
|     pub async fn get_roles_from_db(&self, state: &AppState) -> UserResult<Vec<UserRole>> { | ||||
|         state | ||||
|             .store | ||||
|             .list_user_roles_by_user_id(&self.0.user_id) | ||||
|             .await | ||||
|             .change_context(UserErrors::InternalServerError) | ||||
|     } | ||||
| @ -760,6 +769,29 @@ impl UserFromStorage { | ||||
|         let days_left_for_verification = last_date_for_verification - today; | ||||
|         Ok(Some(days_left_for_verification.whole_days())) | ||||
|     } | ||||
|  | ||||
|     pub fn get_preferred_merchant_id(&self) -> Option<String> { | ||||
|         self.0.preferred_merchant_id.clone() | ||||
|     } | ||||
|  | ||||
|     pub async fn get_role_from_db_by_merchant_id( | ||||
|         &self, | ||||
|         state: &AppState, | ||||
|         merchant_id: &str, | ||||
|     ) -> UserResult<UserRole> { | ||||
|         state | ||||
|             .store | ||||
|             .find_user_role_by_user_id_merchant_id(self.get_user_id(), merchant_id) | ||||
|             .await | ||||
|             .map_err(|e| { | ||||
|                 if e.current_context().is_db_not_found() { | ||||
|                     UserErrors::RoleNotFound | ||||
|                 } else { | ||||
|                     UserErrors::InternalServerError | ||||
|                 } | ||||
|             }) | ||||
|             .into_report() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<info::ModuleInfo> for user_role_api::ModuleInfo { | ||||
| @ -828,3 +860,101 @@ impl TryFrom<UserAndRoleJoined> for user_api::UserDetails { | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub enum SignInWithRoleStrategyType { | ||||
|     SingleRole(SignInWithSingleRoleStrategy), | ||||
|     MultipleRoles(SignInWithMultipleRolesStrategy), | ||||
| } | ||||
|  | ||||
| impl SignInWithRoleStrategyType { | ||||
|     pub async fn decide_signin_strategy_by_user_roles( | ||||
|         user: UserFromStorage, | ||||
|         user_roles: Vec<UserRole>, | ||||
|     ) -> UserResult<Self> { | ||||
|         if user_roles.is_empty() { | ||||
|             return Err(UserErrors::InternalServerError.into()); | ||||
|         } | ||||
|  | ||||
|         if let Some(user_role) = user_roles | ||||
|             .iter() | ||||
|             .find(|role| role.status == UserStatus::Active) | ||||
|         { | ||||
|             Ok(Self::SingleRole(SignInWithSingleRoleStrategy { | ||||
|                 user, | ||||
|                 user_role: user_role.clone(), | ||||
|             })) | ||||
|         } else { | ||||
|             Ok(Self::MultipleRoles(SignInWithMultipleRolesStrategy { | ||||
|                 user, | ||||
|                 user_roles, | ||||
|             })) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub async fn get_signin_response( | ||||
|         self, | ||||
|         state: &AppState, | ||||
|     ) -> UserResult<user_api::SignInResponse> { | ||||
|         match self { | ||||
|             Self::SingleRole(strategy) => strategy.get_signin_response(state).await, | ||||
|             Self::MultipleRoles(strategy) => strategy.get_signin_response(state).await, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct SignInWithSingleRoleStrategy { | ||||
|     pub user: UserFromStorage, | ||||
|     pub user_role: UserRole, | ||||
| } | ||||
|  | ||||
| impl SignInWithSingleRoleStrategy { | ||||
|     async fn get_signin_response(self, state: &AppState) -> UserResult<user_api::SignInResponse> { | ||||
|         let token = | ||||
|             utils::user::generate_jwt_auth_token(state, &self.user, &self.user_role).await?; | ||||
|         let dashboard_entry_response = | ||||
|             utils::user::get_dashboard_entry_response(state, self.user, self.user_role, token)?; | ||||
|         Ok(user_api::SignInResponse::DashboardEntry( | ||||
|             dashboard_entry_response, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct SignInWithMultipleRolesStrategy { | ||||
|     pub user: UserFromStorage, | ||||
|     pub user_roles: Vec<UserRole>, | ||||
| } | ||||
|  | ||||
| impl SignInWithMultipleRolesStrategy { | ||||
|     async fn get_signin_response(self, state: &AppState) -> UserResult<user_api::SignInResponse> { | ||||
|         let merchant_accounts = state | ||||
|             .store | ||||
|             .list_multiple_merchant_accounts( | ||||
|                 self.user_roles | ||||
|                     .iter() | ||||
|                     .map(|role| role.merchant_id.clone()) | ||||
|                     .collect(), | ||||
|             ) | ||||
|             .await | ||||
|             .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
|         let merchant_details = utils::user::get_multiple_merchant_details_with_status( | ||||
|             self.user_roles, | ||||
|             merchant_accounts, | ||||
|         )?; | ||||
|  | ||||
|         Ok(user_api::SignInResponse::MerchantSelect( | ||||
|             user_api::MerchantSelectResponse { | ||||
|                 name: self.user.get_name(), | ||||
|                 email: self.user.get_email(), | ||||
|                 token: auth::UserAuthToken::new_token( | ||||
|                     self.user.get_user_id().to_string(), | ||||
|                     &state.conf, | ||||
|                 ) | ||||
|                 .await? | ||||
|                 .into(), | ||||
|                 merchants: merchant_details, | ||||
|                 verification_days_left: utils::user::get_verification_days_left(state, &self.user)?, | ||||
|             }, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| use std::collections::HashMap; | ||||
|  | ||||
| use api_models::user as user_api; | ||||
| use diesel_models::user_role::UserRole; | ||||
| use diesel_models::{enums::UserStatus, user_role::UserRole}; | ||||
| use error_stack::ResultExt; | ||||
| use masking::Secret; | ||||
|  | ||||
| @ -118,3 +120,29 @@ pub fn get_verification_days_left( | ||||
|     #[cfg(not(feature = "email"))] | ||||
|     return Ok(None); | ||||
| } | ||||
|  | ||||
| pub fn get_multiple_merchant_details_with_status( | ||||
|     user_roles: Vec<UserRole>, | ||||
|     merchant_accounts: Vec<MerchantAccount>, | ||||
| ) -> UserResult<Vec<user_api::UserMerchantAccount>> { | ||||
|     let roles: HashMap<_, _> = user_roles | ||||
|         .into_iter() | ||||
|         .map(|user_role| (user_role.merchant_id.clone(), user_role)) | ||||
|         .collect(); | ||||
|  | ||||
|     merchant_accounts | ||||
|         .into_iter() | ||||
|         .map(|merchant| { | ||||
|             let role = roles | ||||
|                 .get(merchant.merchant_id.as_str()) | ||||
|                 .ok_or(UserErrors::InternalServerError.into()) | ||||
|                 .attach_printable("Merchant exists but user role doesn't")?; | ||||
|  | ||||
|             Ok(user_api::UserMerchantAccount { | ||||
|                 merchant_id: merchant.merchant_id.clone(), | ||||
|                 merchant_name: merchant.merchant_name.clone(), | ||||
|                 is_active: role.status == UserStatus::Active, | ||||
|             }) | ||||
|         }) | ||||
|         .collect() | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| use api_models::user_role as user_role_api; | ||||
| use diesel_models::enums::UserStatus; | ||||
| use diesel_models::{enums::UserStatus, user_role::UserRole}; | ||||
| use error_stack::ResultExt; | ||||
|  | ||||
| use crate::{ | ||||
| @ -17,19 +17,17 @@ pub fn is_internal_role(role_id: &str) -> bool { | ||||
|         || role_id == consts::user_role::ROLE_ID_INTERNAL_VIEW_ONLY_USER | ||||
| } | ||||
|  | ||||
| pub async fn get_merchant_ids_for_user(state: &AppState, user_id: &str) -> UserResult<Vec<String>> { | ||||
| pub async fn get_active_user_roles_for_user( | ||||
|     state: &AppState, | ||||
|     user_id: &str, | ||||
| ) -> UserResult<Vec<UserRole>> { | ||||
|     Ok(state | ||||
|         .store | ||||
|         .list_user_roles_by_user_id(user_id) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)? | ||||
|         .into_iter() | ||||
|         .filter_map(|ele| { | ||||
|             if ele.status == UserStatus::Active { | ||||
|                 return Some(ele.merchant_id); | ||||
|             } | ||||
|             None | ||||
|         }) | ||||
|         .filter(|ele| ele.status == UserStatus::Active) | ||||
|         .collect()) | ||||
| } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Mani Chandra
					Mani Chandra