mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(users): Add preferred_merchant_id column and update user details API (#3373)
				
					
				
			Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
		| @ -253,6 +253,7 @@ pub async fn change_password( | ||||
|                 name: None, | ||||
|                 password: Some(new_password_hash), | ||||
|                 is_verified: None, | ||||
|                 preferred_merchant_id: None, | ||||
|             }, | ||||
|         ) | ||||
|         .await | ||||
| @ -330,6 +331,7 @@ pub async fn reset_password( | ||||
|                 name: None, | ||||
|                 password: Some(hash_password), | ||||
|                 is_verified: Some(true), | ||||
|                 preferred_merchant_id: None, | ||||
|             }, | ||||
|         ) | ||||
|         .await | ||||
| @ -786,3 +788,47 @@ pub async fn verify_token( | ||||
|         user_email: user.email, | ||||
|     })) | ||||
| } | ||||
|  | ||||
| pub async fn update_user_details( | ||||
|     state: AppState, | ||||
|     user_token: auth::UserFromToken, | ||||
|     req: user_api::UpdateUserAccountDetailsRequest, | ||||
| ) -> UserResponse<()> { | ||||
|     let user: domain::UserFromStorage = state | ||||
|         .store | ||||
|         .find_user_by_id(&user_token.user_id) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)? | ||||
|         .into(); | ||||
|  | ||||
|     let name = req.name.map(domain::UserName::new).transpose()?; | ||||
|  | ||||
|     if let Some(ref preferred_merchant_id) = req.preferred_merchant_id { | ||||
|         let _ = state | ||||
|             .store | ||||
|             .find_user_role_by_user_id_merchant_id(user.get_user_id(), preferred_merchant_id) | ||||
|             .await | ||||
|             .map_err(|e| { | ||||
|                 if e.current_context().is_db_not_found() { | ||||
|                     e.change_context(UserErrors::MerchantIdNotFound) | ||||
|                 } else { | ||||
|                     e.change_context(UserErrors::InternalServerError) | ||||
|                 } | ||||
|             })?; | ||||
|     } | ||||
|  | ||||
|     let user_update = storage_user::UserUpdate::AccountUpdate { | ||||
|         name: name.map(|x| x.get_secret().expose()), | ||||
|         password: None, | ||||
|         is_verified: None, | ||||
|         preferred_merchant_id: req.preferred_merchant_id, | ||||
|     }; | ||||
|  | ||||
|     state | ||||
|         .store | ||||
|         .update_user_by_user_id(user.get_user_id(), user_update) | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
|     Ok(ApplicationResponse::StatusOk) | ||||
| } | ||||
|  | ||||
| @ -1927,12 +1927,24 @@ impl UserRoleInterface for KafkaStore { | ||||
|     ) -> CustomResult<user_storage::UserRole, errors::StorageError> { | ||||
|         self.diesel_store.insert_user_role(user_role).await | ||||
|     } | ||||
|  | ||||
|     async fn find_user_role_by_user_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|     ) -> CustomResult<user_storage::UserRole, errors::StorageError> { | ||||
|         self.diesel_store.find_user_role_by_user_id(user_id).await | ||||
|     } | ||||
|  | ||||
|     async fn find_user_role_by_user_id_merchant_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         merchant_id: &str, | ||||
|     ) -> CustomResult<user_storage::UserRole, errors::StorageError> { | ||||
|         self.diesel_store | ||||
|             .find_user_role_by_user_id_merchant_id(user_id, merchant_id) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     async fn update_user_role_by_user_id_merchant_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
| @ -1943,9 +1955,11 @@ impl UserRoleInterface for KafkaStore { | ||||
|             .update_user_role_by_user_id_merchant_id(user_id, merchant_id, update) | ||||
|             .await | ||||
|     } | ||||
|  | ||||
|     async fn delete_user_role(&self, user_id: &str) -> CustomResult<bool, errors::StorageError> { | ||||
|         self.diesel_store.delete_user_role(user_id).await | ||||
|     } | ||||
|  | ||||
|     async fn list_user_roles_by_user_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|  | ||||
| @ -145,6 +145,7 @@ impl UserInterface for MockDb { | ||||
|             is_verified: user_data.is_verified, | ||||
|             created_at: user_data.created_at.unwrap_or(time_now), | ||||
|             last_modified_at: user_data.created_at.unwrap_or(time_now), | ||||
|             preferred_merchant_id: user_data.preferred_merchant_id, | ||||
|         }; | ||||
|         users.push(user.clone()); | ||||
|         Ok(user) | ||||
| @ -207,10 +208,14 @@ impl UserInterface for MockDb { | ||||
|                         name, | ||||
|                         password, | ||||
|                         is_verified, | ||||
|                         preferred_merchant_id, | ||||
|                     } => storage::User { | ||||
|                         name: name.clone().map(Secret::new).unwrap_or(user.name.clone()), | ||||
|                         password: password.clone().unwrap_or(user.password.clone()), | ||||
|                         is_verified: is_verified.unwrap_or(user.is_verified), | ||||
|                         preferred_merchant_id: preferred_merchant_id | ||||
|                             .clone() | ||||
|                             .or(user.preferred_merchant_id.clone()), | ||||
|                         ..user.to_owned() | ||||
|                     }, | ||||
|                 }; | ||||
|  | ||||
| @ -14,16 +14,25 @@ pub trait UserRoleInterface { | ||||
|         &self, | ||||
|         user_role: storage::UserRoleNew, | ||||
|     ) -> CustomResult<storage::UserRole, errors::StorageError>; | ||||
|  | ||||
|     async fn find_user_role_by_user_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|     ) -> CustomResult<storage::UserRole, errors::StorageError>; | ||||
|  | ||||
|     async fn find_user_role_by_user_id_merchant_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         merchant_id: &str, | ||||
|     ) -> CustomResult<storage::UserRole, errors::StorageError>; | ||||
|  | ||||
|     async fn update_user_role_by_user_id_merchant_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         merchant_id: &str, | ||||
|         update: storage::UserRoleUpdate, | ||||
|     ) -> CustomResult<storage::UserRole, errors::StorageError>; | ||||
|  | ||||
|     async fn delete_user_role(&self, user_id: &str) -> CustomResult<bool, errors::StorageError>; | ||||
|  | ||||
|     async fn list_user_roles_by_user_id( | ||||
| @ -57,6 +66,22 @@ impl UserRoleInterface for Store { | ||||
|             .into_report() | ||||
|     } | ||||
|  | ||||
|     async fn find_user_role_by_user_id_merchant_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         merchant_id: &str, | ||||
|     ) -> CustomResult<storage::UserRole, errors::StorageError> { | ||||
|         let conn = connection::pg_connection_write(self).await?; | ||||
|         storage::UserRole::find_by_user_id_merchant_id( | ||||
|             &conn, | ||||
|             user_id.to_owned(), | ||||
|             merchant_id.to_owned(), | ||||
|         ) | ||||
|         .await | ||||
|         .map_err(Into::into) | ||||
|         .into_report() | ||||
|     } | ||||
|  | ||||
|     async fn update_user_role_by_user_id_merchant_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
| @ -148,6 +173,24 @@ impl UserRoleInterface for MockDb { | ||||
|             ) | ||||
|     } | ||||
|  | ||||
|     async fn find_user_role_by_user_id_merchant_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|         merchant_id: &str, | ||||
|     ) -> CustomResult<storage::UserRole, errors::StorageError> { | ||||
|         let user_roles = self.user_roles.lock().await; | ||||
|         user_roles | ||||
|             .iter() | ||||
|             .find(|user_role| user_role.user_id == user_id && user_role.merchant_id == merchant_id) | ||||
|             .cloned() | ||||
|             .ok_or( | ||||
|                 errors::StorageError::ValueNotFound(format!( | ||||
|                     "No user role available for user_id = {user_id} and merchant_id = {merchant_id}" | ||||
|                 )) | ||||
|                 .into(), | ||||
|             ) | ||||
|     } | ||||
|  | ||||
|     async fn update_user_role_by_user_id_merchant_id( | ||||
|         &self, | ||||
|         user_id: &str, | ||||
|  | ||||
| @ -921,6 +921,7 @@ impl User { | ||||
|             .service(web::resource("/role/list").route(web::get().to(list_roles))) | ||||
|             .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("/update").route(web::post().to(update_user_account_details))) | ||||
|             .service( | ||||
|                 web::resource("/data") | ||||
|                     .route(web::get().to(get_multiple_dashboard_metadata)) | ||||
|  | ||||
| @ -178,7 +178,8 @@ impl From<Flow> for ApiIdentifier { | ||||
|             | Flow::InviteUser | ||||
|             | Flow::UserSignUpWithMerchantId | ||||
|             | Flow::VerifyEmail | ||||
|             | Flow::VerifyEmailRequest => Self::User, | ||||
|             | Flow::VerifyEmailRequest | ||||
|             | Flow::UpdateUserAccountDetails => Self::User, | ||||
|  | ||||
|             Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => { | ||||
|                 Self::UserRole | ||||
|  | ||||
| @ -403,3 +403,21 @@ pub async fn verify_recon_token(state: web::Data<AppState>, http_req: HttpReques | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
| pub async fn update_user_account_details( | ||||
|     state: web::Data<AppState>, | ||||
|     req: HttpRequest, | ||||
|     json_payload: web::Json<user_api::UpdateUserAccountDetailsRequest>, | ||||
| ) -> HttpResponse { | ||||
|     let flow = Flow::UpdateUserAccountDetails; | ||||
|     Box::pin(api::server_wrap( | ||||
|         flow, | ||||
|         state.clone(), | ||||
|         &req, | ||||
|         json_payload.into_inner(), | ||||
|         user_core::update_user_details, | ||||
|         &auth::DashboardNoPermissionAuth, | ||||
|         api_locking::LockAction::NotApplicable, | ||||
|     )) | ||||
|     .await | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Mani Chandra
					Mani Chandra