mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	fix(users): remove internal entity type for users (#6013)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
		| @ -3144,7 +3144,6 @@ pub enum ApiVersion { | ||||
| #[strum(serialize_all = "snake_case")] | ||||
| #[serde(rename_all = "snake_case")] | ||||
| pub enum EntityType { | ||||
|     Internal = 3, | ||||
|     Organization = 2, | ||||
|     Merchant = 1, | ||||
|     Profile = 0, | ||||
|  | ||||
| @ -33,11 +33,6 @@ impl UserRole { | ||||
|                 let org_id = self.org_id.clone()?.get_string_repr().to_string(); | ||||
|                 Some((org_id, EntityType::Organization)) | ||||
|             } | ||||
|             (enums::UserRoleVersion::V1, consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER) | ||||
|             | (enums::UserRoleVersion::V1, consts::ROLE_ID_INTERNAL_ADMIN) => { | ||||
|                 let merchant_id = self.merchant_id.clone()?.get_string_repr().to_string(); | ||||
|                 Some((merchant_id, EntityType::Internal)) | ||||
|             } | ||||
|             (enums::UserRoleVersion::V1, _) => { | ||||
|                 let merchant_id = self.merchant_id.clone()?.get_string_repr().to_string(); | ||||
|                 Some((merchant_id, EntityType::Merchant)) | ||||
|  | ||||
| @ -650,7 +650,6 @@ async fn handle_existing_user_invitation( | ||||
|     }; | ||||
|  | ||||
|     let _user_role = match role_info.get_entity_type() { | ||||
|         EntityType::Internal => return Err(UserErrors::InvalidRoleId.into()), | ||||
|         EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()), | ||||
|         EntityType::Merchant => { | ||||
|             user_role | ||||
| @ -682,7 +681,6 @@ async fn handle_existing_user_invitation( | ||||
|     { | ||||
|         let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?; | ||||
|         let entity = match role_info.get_entity_type() { | ||||
|             EntityType::Internal => return Err(UserErrors::InvalidRoleId.into()), | ||||
|             EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()), | ||||
|             EntityType::Merchant => email_types::Entity { | ||||
|                 entity_id: user_from_token.merchant_id.get_string_repr().to_owned(), | ||||
| @ -769,7 +767,6 @@ async fn handle_new_user_invitation( | ||||
|     }; | ||||
|  | ||||
|     let _user_role = match role_info.get_entity_type() { | ||||
|         EntityType::Internal => return Err(UserErrors::InvalidRoleId.into()), | ||||
|         EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()), | ||||
|         EntityType::Merchant => { | ||||
|             user_role | ||||
| @ -805,7 +802,6 @@ async fn handle_new_user_invitation( | ||||
|         let _ = req_state.clone(); | ||||
|         let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?; | ||||
|         let entity = match role_info.get_entity_type() { | ||||
|             EntityType::Internal => return Err(UserErrors::InvalidRoleId.into()), | ||||
|             EntityType::Organization => return Err(UserErrors::InvalidRoleId.into()), | ||||
|             EntityType::Merchant => email_types::Entity { | ||||
|                 entity_id: user_from_token.merchant_id.get_string_repr().to_owned(), | ||||
| @ -1089,15 +1085,13 @@ pub async fn create_internal_user( | ||||
|             } | ||||
|         })?; | ||||
|  | ||||
|     let internal_merchant_id = common_utils::id_type::MerchantId::get_internal_user_merchant_id( | ||||
|         consts::user_role::INTERNAL_USER_MERCHANT_ID, | ||||
|     ); | ||||
|  | ||||
|     let internal_merchant = state | ||||
|         .store | ||||
|         .find_merchant_account_by_merchant_id( | ||||
|             key_manager_state, | ||||
|             &common_utils::id_type::MerchantId::get_internal_user_merchant_id( | ||||
|                 consts::user_role::INTERNAL_USER_MERCHANT_ID, | ||||
|             ), | ||||
|             &key_store, | ||||
|         ) | ||||
|         .find_merchant_account_by_merchant_id(key_manager_state, &internal_merchant_id, &key_store) | ||||
|         .await | ||||
|         .map_err(|e| { | ||||
|             if e.current_context().is_db_not_found() { | ||||
| @ -1130,8 +1124,9 @@ pub async fn create_internal_user( | ||||
|             common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), | ||||
|             UserStatus::Active, | ||||
|         ) | ||||
|         .add_entity(domain::InternalLevel { | ||||
|         .add_entity(domain::MerchantLevel { | ||||
|             org_id: internal_merchant.organization_id, | ||||
|             merchant_id: internal_merchant_id, | ||||
|         }) | ||||
|         .insert_in_v1_and_v2(&state) | ||||
|         .await | ||||
| @ -1443,6 +1438,13 @@ pub async fn list_user_roles_details( | ||||
|     .to_not_found_response(UserErrors::InternalServerError) | ||||
|     .attach_printable("Failed to fetch role info")?; | ||||
|  | ||||
|     if requestor_role_info.is_internal() { | ||||
|         return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|             "Internal roles are not allowed for this operation".to_string(), | ||||
|         ) | ||||
|         .into()); | ||||
|     } | ||||
|  | ||||
|     let user_roles_set = state | ||||
|         .store | ||||
|         .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { | ||||
| @ -1517,12 +1519,6 @@ pub async fn list_user_roles_details( | ||||
|                     merchant.push(merchant_id.clone()); | ||||
|                     merchant_profile.push((merchant_id, profile_id)) | ||||
|                 } | ||||
|                 EntityType::Internal => { | ||||
|                     return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|                         "Internal roles are not allowed for this operation".to_string(), | ||||
|                     ) | ||||
|                     .into()); | ||||
|                 } | ||||
|                 EntityType::Organization => (), | ||||
|             }; | ||||
|  | ||||
| @ -1609,11 +1605,6 @@ pub async fn list_user_roles_details( | ||||
|                 .ok_or(UserErrors::InternalServerError)?; | ||||
|  | ||||
|             let (merchant, profile) = match entity_type { | ||||
|                 EntityType::Internal => { | ||||
|                     return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|                         "Internal roles are not allowed for this operation".to_string(), | ||||
|                     )); | ||||
|                 } | ||||
|                 EntityType::Organization => (None, None), | ||||
|                 EntityType::Merchant => { | ||||
|                     let merchant_id = &user_role | ||||
| @ -2623,9 +2614,14 @@ pub async fn list_orgs_for_user( | ||||
|     .await | ||||
|     .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
|     let orgs = match role_info.get_entity_type() { | ||||
|         EntityType::Internal => return Err(UserErrors::InvalidRoleOperation.into()), | ||||
|         EntityType::Organization | EntityType::Merchant | EntityType::Profile => state | ||||
|     if role_info.is_internal() { | ||||
|         return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|             "Internal roles are not allowed for this operation".to_string(), | ||||
|         ) | ||||
|         .into()); | ||||
|     } | ||||
|  | ||||
|     let orgs = state | ||||
|         .store | ||||
|         .list_user_roles_by_user_id(ListUserRolesByUserIdPayload { | ||||
|             user_id: user_from_token.user_id.as_str(), | ||||
| @ -2641,8 +2637,7 @@ pub async fn list_orgs_for_user( | ||||
|         .change_context(UserErrors::InternalServerError)? | ||||
|         .into_iter() | ||||
|         .filter_map(|user_role| user_role.org_id) | ||||
|             .collect::<HashSet<_>>(), | ||||
|     }; | ||||
|         .collect::<HashSet<_>>(); | ||||
|  | ||||
|     let resp = futures::future::try_join_all( | ||||
|         orgs.iter() | ||||
| @ -2676,8 +2671,16 @@ pub async fn list_merchants_for_user_in_org( | ||||
|     ) | ||||
|     .await | ||||
|     .change_context(UserErrors::InternalServerError)?; | ||||
|  | ||||
|     if role_info.is_internal() { | ||||
|         return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|             "Internal roles are not allowed for this operation".to_string(), | ||||
|         ) | ||||
|         .into()); | ||||
|     } | ||||
|  | ||||
|     let merchant_accounts = match role_info.get_entity_type() { | ||||
|         EntityType::Organization | EntityType::Internal => state | ||||
|         EntityType::Organization => state | ||||
|             .store | ||||
|             .list_merchant_accounts_by_organization_id(&(&state).into(), &user_from_token.org_id) | ||||
|             .await | ||||
| @ -2752,7 +2755,7 @@ pub async fn list_profiles_for_user_in_org_and_merchant_account( | ||||
|         .await | ||||
|         .change_context(UserErrors::InternalServerError)?; | ||||
|     let profiles = match role_info.get_entity_type() { | ||||
|         EntityType::Organization | EntityType::Merchant | EntityType::Internal => state | ||||
|         EntityType::Organization | EntityType::Merchant => state | ||||
|             .store | ||||
|             .list_profile_by_merchant_id( | ||||
|                 key_manager_state, | ||||
| @ -2831,7 +2834,7 @@ pub async fn switch_org_for_user( | ||||
|     .change_context(UserErrors::InternalServerError) | ||||
|     .attach_printable("Failed to retrieve role information")?; | ||||
|  | ||||
|     if role_info.get_entity_type() == EntityType::Internal { | ||||
|     if role_info.is_internal() { | ||||
|         return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|             "Org switching not allowed for Internal role".to_string(), | ||||
|         ) | ||||
| @ -2910,8 +2913,8 @@ pub async fn switch_merchant_for_user_in_org( | ||||
|     .change_context(UserErrors::InternalServerError) | ||||
|     .attach_printable("Failed to retrieve role information")?; | ||||
|  | ||||
|     let (org_id, merchant_id, profile_id, role_id) = match role_info.get_entity_type() { | ||||
|         EntityType::Internal => { | ||||
|     // Check if the role is internal and handle separately | ||||
|     let (org_id, merchant_id, profile_id, role_id) = if role_info.is_internal() { | ||||
|         let merchant_key_store = state | ||||
|             .store | ||||
|             .get_merchant_key_store_by_merchant_id( | ||||
| @ -2954,8 +2957,9 @@ pub async fn switch_merchant_for_user_in_org( | ||||
|             profile_id, | ||||
|             user_from_token.role_id.clone(), | ||||
|         ) | ||||
|         } | ||||
|  | ||||
|     } else { | ||||
|         // Match based on the other entity types | ||||
|         match role_info.get_entity_type() { | ||||
|             EntityType::Organization => { | ||||
|                 let merchant_key_store = state | ||||
|                     .store | ||||
| @ -2987,7 +2991,11 @@ pub async fn switch_merchant_for_user_in_org( | ||||
|  | ||||
|                 let profile_id = state | ||||
|                     .store | ||||
|                 .list_profile_by_merchant_id(key_manager_state, &merchant_key_store, &merchant_id) | ||||
|                     .list_profile_by_merchant_id( | ||||
|                         key_manager_state, | ||||
|                         &merchant_key_store, | ||||
|                         &merchant_id, | ||||
|                     ) | ||||
|                     .await | ||||
|                     .change_context(UserErrors::InternalServerError) | ||||
|                     .attach_printable("Failed to list business profiles by merchant_id")? | ||||
| @ -3027,43 +3035,17 @@ pub async fn switch_merchant_for_user_in_org( | ||||
|                         "No user role associated with the requested merchant_id".to_string(), | ||||
|                     ))?; | ||||
|  | ||||
|             let profile_id = if let Some(profile_id) = &user_role.profile_id { | ||||
|                 profile_id.clone() | ||||
|             } else { | ||||
|                 let merchant_key_store = state | ||||
|                     .store | ||||
|                     .get_merchant_key_store_by_merchant_id( | ||||
|                         key_manager_state, | ||||
|                         &request.merchant_id, | ||||
|                         &state.store.get_master_key().to_vec().into(), | ||||
|                     ) | ||||
|                     .await | ||||
|                     .change_context(UserErrors::InternalServerError) | ||||
|                     .attach_printable("Failed to retrieve merchant key store by merchant_id")?; | ||||
|  | ||||
|                 state | ||||
|                     .store | ||||
|                     .list_profile_by_merchant_id( | ||||
|                         key_manager_state, | ||||
|                         &merchant_key_store, | ||||
|                         &request.merchant_id, | ||||
|                     ) | ||||
|                     .await | ||||
|                     .change_context(UserErrors::InternalServerError) | ||||
|                     .attach_printable("Failed to list business profiles for the given merchant_id")? | ||||
|                     .pop() | ||||
|                     .ok_or(UserErrors::InternalServerError) | ||||
|                     .attach_printable("No business profile found for the given merchant_id")? | ||||
|                     .get_id() | ||||
|                     .to_owned() | ||||
|             }; | ||||
|                 let (merchant_id, profile_id) = | ||||
|                     utils::user_role::get_single_merchant_id_and_profile_id(&state, &user_role) | ||||
|                         .await?; | ||||
|                 ( | ||||
|                     user_from_token.org_id, | ||||
|                 request.merchant_id, | ||||
|                     merchant_id, | ||||
|                     profile_id, | ||||
|                     user_role.role_id, | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let token = utils::user::generate_jwt_auth_token_with_attributes( | ||||
| @ -3116,7 +3098,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant( | ||||
|     .attach_printable("Failed to retrieve role information")?; | ||||
|  | ||||
|     let (profile_id, role_id) = match role_info.get_entity_type() { | ||||
|         EntityType::Internal | EntityType::Organization | EntityType::Merchant => { | ||||
|         EntityType::Organization | EntityType::Merchant => { | ||||
|             let merchant_key_store = state | ||||
|                 .store | ||||
|                 .get_merchant_key_store_by_merchant_id( | ||||
|  | ||||
| @ -734,7 +734,6 @@ pub async fn list_users_in_lineage( | ||||
|             ) | ||||
|             .await? | ||||
|         } | ||||
|         EntityType::Internal => HashSet::new(), | ||||
|     }; | ||||
|  | ||||
|     let mut email_map = state | ||||
| @ -859,10 +858,13 @@ pub async fn list_invitations_for_user( | ||||
|                         .clone() | ||||
|                         .ok_or(UserErrors::InternalServerError)?, | ||||
|                 )), | ||||
|                 EntityType::Internal => return Err(report!(UserErrors::InternalServerError)), | ||||
|             } | ||||
|  | ||||
|             Ok((org_ids, merchant_ids, profile_ids_with_merchant_ids)) | ||||
|             Ok::<_, error_stack::Report<UserErrors>>(( | ||||
|                 org_ids, | ||||
|                 merchant_ids, | ||||
|                 profile_ids_with_merchant_ids, | ||||
|             )) | ||||
|         }, | ||||
|     )?; | ||||
|  | ||||
| @ -953,7 +955,6 @@ pub async fn list_invitations_for_user( | ||||
|                     .as_ref() | ||||
|                     .map(|profile_id| profile_name_map.get(profile_id).cloned()) | ||||
|                     .ok_or(UserErrors::InternalServerError)?, | ||||
|                 EntityType::Internal => return Err(report!(UserErrors::InternalServerError)), | ||||
|             }; | ||||
|  | ||||
|             Ok(user_role_api::ListInvitationForUserResponse { | ||||
|  | ||||
| @ -224,6 +224,13 @@ pub async fn list_roles_with_info( | ||||
|         .await | ||||
|         .attach_printable("Invalid role_id in JWT")?; | ||||
|  | ||||
|     if user_role_info.is_internal() { | ||||
|         return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|             "Internal roles are not allowed for this operation".to_string(), | ||||
|         ) | ||||
|         .into()); | ||||
|     } | ||||
|  | ||||
|     let mut role_info_vec = PREDEFINED_ROLES | ||||
|         .iter() | ||||
|         .map(|(_, role_info)| role_info.clone()) | ||||
| @ -256,12 +263,6 @@ pub async fn list_roles_with_info( | ||||
|                 .attach_printable("Failed to get roles")?, | ||||
|             // TODO: Populate this from Db function when support for profile id and profile level custom roles is added | ||||
|             EntityType::Profile => Vec::new(), | ||||
|             EntityType::Internal => { | ||||
|                 return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|                     "Internal roles are not allowed for this operation".to_string(), | ||||
|                 ) | ||||
|                 .into()); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|     role_info_vec.extend(custom_roles.into_iter().map(roles::RoleInfo::from)); | ||||
| @ -336,13 +337,6 @@ pub async fn list_roles_at_entity_level( | ||||
|             .attach_printable("Failed to get roles")?, | ||||
|         // TODO: Populate this from Db function when support for profile id and profile level custom roles is added | ||||
|         EntityType::Profile => Vec::new(), | ||||
|  | ||||
|         EntityType::Internal => { | ||||
|             return Err(UserErrors::InvalidRoleOperationWithMessage( | ||||
|                 "Internal roles are not allowed for this operation".to_string(), | ||||
|             ) | ||||
|             .into()); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     role_info_vec.extend(custom_roles.into_iter().map(roles::RoleInfo::from)); | ||||
|  | ||||
| @ -31,7 +31,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(| | ||||
|             role_id: common_utils::consts::ROLE_ID_INTERNAL_ADMIN.to_string(), | ||||
|             role_name: "internal_admin".to_string(), | ||||
|             scope: RoleScope::Organization, | ||||
|             entity_type: EntityType::Internal, | ||||
|             entity_type: EntityType::Merchant, | ||||
|             is_invitable: false, | ||||
|             is_deletable: false, | ||||
|             is_updatable: false, | ||||
| @ -52,7 +52,7 @@ pub static PREDEFINED_ROLES: Lazy<HashMap<&'static str, RoleInfo>> = Lazy::new(| | ||||
|             role_id: common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), | ||||
|             role_name: "internal_view_only".to_string(), | ||||
|             scope: RoleScope::Organization, | ||||
|             entity_type: EntityType::Internal, | ||||
|             entity_type: EntityType::Merchant, | ||||
|             is_invitable: false, | ||||
|             is_deletable: false, | ||||
|             is_updatable: false, | ||||
|  | ||||
| @ -1136,11 +1136,6 @@ pub struct ProfileLevel { | ||||
|     pub profile_id: id_type::ProfileId, | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct InternalLevel { | ||||
|     pub org_id: id_type::OrganizationId, | ||||
| } | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct NewUserRole<E: Clone> { | ||||
|     pub user_id: String, | ||||
| @ -1316,29 +1311,6 @@ impl NewUserRole<MerchantLevel> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NewUserRole<InternalLevel> { | ||||
|     pub async fn insert_in_v1_and_v2(self, state: &SessionState) -> UserResult<UserRole> { | ||||
|         let entity = self.entity.clone(); | ||||
|         let internal_merchant_id = id_type::MerchantId::get_internal_user_merchant_id( | ||||
|             consts::user_role::INTERNAL_USER_MERCHANT_ID, | ||||
|         ); | ||||
|  | ||||
|         let new_v1_role = self | ||||
|             .clone() | ||||
|             .convert_to_new_v1_role(entity.org_id.clone(), internal_merchant_id.clone()); | ||||
|  | ||||
|         let new_v2_role = self.convert_to_new_v2_role(EntityInfo { | ||||
|             org_id: entity.org_id.clone(), | ||||
|             merchant_id: Some(internal_merchant_id.clone()), | ||||
|             profile_id: None, | ||||
|             entity_id: internal_merchant_id.get_string_repr().to_owned(), | ||||
|             entity_type: EntityType::Internal, | ||||
|         }); | ||||
|  | ||||
|         Self::insert_v1_and_v2_in_db_and_get_v2(state, new_v1_role, new_v2_role).await | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NewUserRole<ProfileLevel> { | ||||
|     pub async fn insert_in_v2(self, state: &SessionState) -> UserResult<UserRole> { | ||||
|         let entity = self.entity.clone(); | ||||
|  | ||||
| @ -239,10 +239,7 @@ pub async fn get_single_merchant_id( | ||||
|             .attach_printable("No merchants found for org_id")? | ||||
|             .get_id() | ||||
|             .clone()), | ||||
|         Some(EntityType::Merchant) | ||||
|         | Some(EntityType::Internal) | ||||
|         | Some(EntityType::Profile) | ||||
|         | None => user_role | ||||
|         Some(EntityType::Merchant) | Some(EntityType::Profile) | None => user_role | ||||
|             .merchant_id | ||||
|             .clone() | ||||
|             .ok_or(UserErrors::InternalServerError) | ||||
| @ -263,9 +260,7 @@ pub async fn get_lineage_for_user_id_and_entity_for_accepting_invite( | ||||
|     )>, | ||||
| > { | ||||
|     match entity_type { | ||||
|         EntityType::Internal | EntityType::Organization => { | ||||
|             Err(UserErrors::InvalidRoleOperation.into()) | ||||
|         } | ||||
|         EntityType::Organization => Err(UserErrors::InvalidRoleOperation.into()), | ||||
|         EntityType::Merchant => { | ||||
|             let Ok(merchant_id) = id_type::MerchantId::wrap(entity_id) else { | ||||
|                 return Ok(None); | ||||
| @ -369,7 +364,7 @@ pub async fn get_single_merchant_id_and_profile_id( | ||||
|         .get_entity_id_and_type() | ||||
|         .ok_or(UserErrors::InternalServerError)?; | ||||
|     let profile_id = match entity_type { | ||||
|         EntityType::Organization | EntityType::Merchant | EntityType::Internal => { | ||||
|         EntityType::Organization | EntityType::Merchant => { | ||||
|             let key_store = state | ||||
|                 .store | ||||
|                 .get_merchant_key_store_by_merchant_id( | ||||
| @ -438,14 +433,6 @@ pub fn get_min_entity( | ||||
|         | (EntityType::Merchant, Some(EntityType::Profile)) | ||||
|         | (EntityType::Profile, Some(EntityType::Profile)) => Ok(EntityType::Profile), | ||||
|  | ||||
|         (EntityType::Internal, _) => Ok(EntityType::Internal), | ||||
|  | ||||
|         (EntityType::Organization, Some(EntityType::Internal)) | ||||
|         | (EntityType::Merchant, Some(EntityType::Internal)) | ||||
|         | (EntityType::Profile, Some(EntityType::Internal)) => { | ||||
|             Err(UserErrors::InvalidRoleOperation.into()) | ||||
|         } | ||||
|  | ||||
|         (EntityType::Merchant, Some(EntityType::Organization)) | ||||
|         | (EntityType::Profile, Some(EntityType::Organization)) | ||||
|         | (EntityType::Profile, Some(EntityType::Merchant)) => { | ||||
|  | ||||
| @ -0,0 +1,2 @@ | ||||
| -- This file should undo anything in `up.sql` | ||||
| UPDATE user_roles SET entity_type = 'internal' where role_id like 'internal%' and version = 'v2'; | ||||
| @ -0,0 +1,2 @@ | ||||
| -- Your SQL goes here | ||||
| UPDATE user_roles SET entity_type = 'merchant' WHERE entity_type = 'internal'; | ||||
		Reference in New Issue
	
	Block a user
	 Apoorv Dixit
					Apoorv Dixit