feat(user_role): Insert V2 user_roles (#5607)

This commit is contained in:
Mani Chandra
2024-08-29 20:12:04 +05:30
committed by GitHub
parent 35666f57bf
commit 6c266b5df4
7 changed files with 474 additions and 141 deletions

View File

@ -5,6 +5,7 @@ use diesel::{
BoolExpressionMethods, ExpressionMethods, QueryDsl, BoolExpressionMethods, ExpressionMethods, QueryDsl,
}; };
use error_stack::{report, ResultExt}; use error_stack::{report, ResultExt};
use router_env::logger;
use crate::{ use crate::{
enums::UserRoleVersion, errors, query::generics, schema::user_roles::dsl, user_role::*, enums::UserRoleVersion, errors, query::generics, schema::user_roles::dsl, user_role::*,
@ -18,6 +19,21 @@ impl UserRoleNew {
} }
impl UserRole { impl UserRole {
pub async fn insert_multiple_user_roles(
conn: &PgPooledConn,
user_roles: Vec<UserRoleNew>,
) -> StorageResult<Vec<Self>> {
let query = diesel::insert_into(<Self>::table()).values(user_roles);
logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());
query
.get_results_async(conn)
.await
.change_context(errors::DatabaseError::Others)
.attach_printable("Error while inserting user_roles")
}
pub async fn find_by_user_id( pub async fn find_by_user_id(
conn: &PgPooledConn, conn: &PgPooledConn,
user_id: String, user_id: String,

View File

@ -1,4 +1,7 @@
use std::collections::{HashMap, HashSet}; use std::{
collections::{HashMap, HashSet},
ops::Not,
};
use api_models::{ use api_models::{
payments::RedirectionResponse, payments::RedirectionResponse,
@ -13,7 +16,6 @@ use diesel_models::{
organization::OrganizationBridge, organization::OrganizationBridge,
user as storage_user, user as storage_user,
user_authentication_method::{UserAuthenticationMethodNew, UserAuthenticationMethodUpdate}, user_authentication_method::{UserAuthenticationMethodNew, UserAuthenticationMethodUpdate},
user_role::UserRoleNew,
}; };
use error_stack::{report, ResultExt}; use error_stack::{report, ResultExt};
#[cfg(feature = "email")] #[cfg(feature = "email")]
@ -60,10 +62,11 @@ pub async fn signup_with_merchant_id(
.await?; .await?;
let user_role = new_user let user_role = new_user
.insert_user_role_in_db( .insert_org_level_user_role_in_db(
state.clone(), state.clone(),
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
UserStatus::Active, UserStatus::Active,
None,
) )
.await?; .await?;
@ -132,10 +135,11 @@ pub async fn signup(
.insert_user_and_merchant_in_db(state.clone()) .insert_user_and_merchant_in_db(state.clone())
.await?; .await?;
let user_role = new_user let user_role = new_user
.insert_user_role_in_db( .insert_org_level_user_role_in_db(
state.clone(), state.clone(),
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
UserStatus::Active, UserStatus::Active,
None,
) )
.await?; .await?;
utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await; utils::user_role::set_role_permissions_in_cache_by_user_role(&state, &user_role).await;
@ -163,10 +167,11 @@ pub async fn signup_token_only_flow(
.insert_user_and_merchant_in_db(state.clone()) .insert_user_and_merchant_in_db(state.clone())
.await?; .await?;
let user_role = new_user let user_role = new_user
.insert_user_role_in_db( .insert_org_level_user_role_in_db(
state.clone(), state.clone(),
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
UserStatus::Active, UserStatus::Active,
None,
) )
.await?; .await?;
@ -314,10 +319,11 @@ pub async fn connect_account(
.insert_user_and_merchant_in_db(state.clone()) .insert_user_and_merchant_in_db(state.clone())
.await?; .await?;
let user_role = new_user let user_role = new_user
.insert_user_role_in_db( .insert_org_level_user_role_in_db(
state.clone(), state.clone(),
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
UserStatus::Active, UserStatus::Active,
None,
) )
.await?; .await?;
@ -773,13 +779,42 @@ async fn handle_existing_user_invitation(
auth_id: &Option<String>, auth_id: &Option<String>,
) -> UserResult<InviteMultipleUserResponse> { ) -> UserResult<InviteMultipleUserResponse> {
let now = common_utils::date_time::now(); let now = common_utils::date_time::now();
state
if state
.store .store
.insert_user_role(UserRoleNew { .find_user_role_by_user_id_and_lineage(
invitee_user_from_db.get_user_id(),
&user_from_token.org_id,
&user_from_token.merchant_id,
user_from_token.profile_id.as_ref(),
UserRoleVersion::V1,
)
.await
.is_err_and(|err| err.current_context().is_db_not_found())
.not()
{
return Err(UserErrors::UserExists.into());
}
if state
.store
.find_user_role_by_user_id_and_lineage(
invitee_user_from_db.get_user_id(),
&user_from_token.org_id,
&user_from_token.merchant_id,
user_from_token.profile_id.as_ref(),
UserRoleVersion::V2,
)
.await
.is_err_and(|err| err.current_context().is_db_not_found())
.not()
{
return Err(UserErrors::UserExists.into());
}
let user_role = domain::NewUserRole {
user_id: invitee_user_from_db.get_user_id().to_owned(), user_id: invitee_user_from_db.get_user_id().to_owned(),
merchant_id: Some(user_from_token.merchant_id.clone()),
role_id: request.role_id.clone(), role_id: request.role_id.clone(),
org_id: Some(user_from_token.org_id.clone()),
status: { status: {
if cfg!(feature = "email") { if cfg!(feature = "email") {
UserStatus::InvitationSent UserStatus::InvitationSent
@ -791,19 +826,13 @@ async fn handle_existing_user_invitation(
last_modified_by: user_from_token.user_id.clone(), last_modified_by: user_from_token.user_id.clone(),
created_at: now, created_at: now,
last_modified: now, last_modified: now,
profile_id: None, entity: domain::MerchantLevel {
entity_id: None, org_id: user_from_token.org_id.clone(),
entity_type: None, merchant_id: user_from_token.merchant_id.clone(),
version: UserRoleVersion::V1, },
})
.await
.map_err(|e| {
if e.current_context().is_db_unique_violation() {
e.change_context(UserErrors::UserExists)
} else {
e.change_context(UserErrors::InternalServerError)
} }
})?; .insert_in_v1_and_v2(state)
.await?;
let is_email_sent; let is_email_sent;
#[cfg(feature = "email")] #[cfg(feature = "email")]
@ -865,31 +894,22 @@ async fn handle_new_user_invitation(
}; };
let now = common_utils::date_time::now(); let now = common_utils::date_time::now();
state
.store let user_role = domain::NewUserRole {
.insert_user_role(UserRoleNew {
user_id: new_user.get_user_id().to_owned(), user_id: new_user.get_user_id().to_owned(),
merchant_id: Some(user_from_token.merchant_id.clone()),
role_id: request.role_id.clone(), role_id: request.role_id.clone(),
org_id: Some(user_from_token.org_id.clone()),
status: invitation_status, status: invitation_status,
created_by: user_from_token.user_id.clone(), created_by: user_from_token.user_id.clone(),
last_modified_by: user_from_token.user_id.clone(), last_modified_by: user_from_token.user_id.clone(),
created_at: now, created_at: now,
last_modified: now, last_modified: now,
profile_id: None, entity: domain::MerchantLevel {
entity_id: None, merchant_id: user_from_token.merchant_id.clone(),
entity_type: None, org_id: user_from_token.org_id.clone(),
version: UserRoleVersion::V1, },
})
.await
.map_err(|e| {
if e.current_context().is_db_unique_violation() {
e.change_context(UserErrors::UserExists)
} else {
e.change_context(UserErrors::InternalServerError)
} }
})?; .insert_in_v1_and_v2(state)
.await?;
let is_email_sent; let is_email_sent;
// TODO: Adding this to avoid clippy lints, remove this once the token only flow is being used // TODO: Adding this to avoid clippy lints, remove this once the token only flow is being used
@ -1289,7 +1309,7 @@ pub async fn create_internal_user(
} }
})?; })?;
let new_user = domain::NewUser::try_from((request, internal_merchant.organization_id))?; let new_user = domain::NewUser::try_from((request, internal_merchant.organization_id.clone()))?;
let mut store_user: storage_user::UserNew = new_user.clone().try_into()?; let mut store_user: storage_user::UserNew = new_user.clone().try_into()?;
store_user.set_is_verified(true); store_user.set_is_verified(true);
@ -1308,12 +1328,16 @@ pub async fn create_internal_user(
.map(domain::user::UserFromStorage::from)?; .map(domain::user::UserFromStorage::from)?;
new_user new_user
.insert_user_role_in_db( .get_no_level_user_role(
state,
common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(), common_utils::consts::ROLE_ID_INTERNAL_VIEW_ONLY_USER.to_string(),
UserStatus::Active, UserStatus::Active,
) )
.await?; .add_entity(domain::InternalLevel {
org_id: internal_merchant.organization_id,
})
.insert_in_v1_and_v2(&state)
.await
.change_context(UserErrors::InternalServerError)?;
Ok(ApplicationResponse::StatusOk) Ok(ApplicationResponse::StatusOk)
} }
@ -1448,10 +1472,11 @@ pub async fn create_merchant_account(
.await?; .await?;
let role_insertion_res = new_user let role_insertion_res = new_user
.insert_user_role_in_db( .insert_org_level_user_role_in_db(
state.clone(), state.clone(),
common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(), common_utils::consts::ROLE_ID_ORGANIZATION_ADMIN.to_string(),
UserStatus::Active, UserStatus::Active,
Some(UserRoleVersion::V1),
) )
.await; .await;
if let Err(e) = role_insertion_res { if let Err(e) = role_insertion_res {

View File

@ -35,7 +35,7 @@ use super::{
user::{sample_data::BatchSampleDataInterface, UserInterface}, user::{sample_data::BatchSampleDataInterface, UserInterface},
user_authentication_method::UserAuthenticationMethodInterface, user_authentication_method::UserAuthenticationMethodInterface,
user_key_store::UserKeyStoreInterface, user_key_store::UserKeyStoreInterface,
user_role::{ListUserRolesByOrgIdPayload, UserRoleInterface}, user_role::{InsertUserRolePayload, ListUserRolesByOrgIdPayload, UserRoleInterface},
}; };
#[cfg(feature = "payouts")] #[cfg(feature = "payouts")]
use crate::services::kafka::payout::KafkaPayout; use crate::services::kafka::payout::KafkaPayout;
@ -2767,8 +2767,8 @@ impl RedisConnInterface for KafkaStore {
impl UserRoleInterface for KafkaStore { impl UserRoleInterface for KafkaStore {
async fn insert_user_role( async fn insert_user_role(
&self, &self,
user_role: user_storage::UserRoleNew, user_role: InsertUserRolePayload,
) -> CustomResult<user_storage::UserRole, errors::StorageError> { ) -> CustomResult<Vec<user_storage::UserRole>, errors::StorageError> {
self.diesel_store.insert_user_role(user_role).await self.diesel_store.insert_user_role(user_role).await
} }

View File

@ -10,12 +10,35 @@ use crate::{
services::Store, services::Store,
}; };
pub enum InsertUserRolePayload {
OnlyV1(storage::UserRoleNew),
OnlyV2(storage::UserRoleNew),
V1AndV2(Box<[storage::UserRoleNew; 2]>),
}
impl InsertUserRolePayload {
fn convert_to_vec(self) -> Vec<storage::UserRoleNew> {
match self {
Self::OnlyV1(user_role) | Self::OnlyV2(user_role) => vec![user_role],
Self::V1AndV2(user_roles) => user_roles.to_vec(),
}
}
}
pub struct ListUserRolesByOrgIdPayload<'a> {
pub user_id: Option<&'a String>,
pub org_id: &'a id_type::OrganizationId,
pub merchant_id: Option<&'a id_type::MerchantId>,
pub profile_id: Option<&'a id_type::ProfileId>,
pub version: Option<enums::UserRoleVersion>,
}
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait UserRoleInterface { pub trait UserRoleInterface {
async fn insert_user_role( async fn insert_user_role(
&self, &self,
user_role: storage::UserRoleNew, user_role: InsertUserRolePayload,
) -> CustomResult<storage::UserRole, errors::StorageError>; ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>;
async fn find_user_role_by_user_id( async fn find_user_role_by_user_id(
&self, &self,
@ -86,24 +109,16 @@ pub trait UserRoleInterface {
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>; ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>;
} }
pub struct ListUserRolesByOrgIdPayload<'a> {
pub user_id: Option<&'a String>,
pub org_id: &'a id_type::OrganizationId,
pub merchant_id: Option<&'a id_type::MerchantId>,
pub profile_id: Option<&'a id_type::ProfileId>,
pub version: Option<enums::UserRoleVersion>,
}
#[async_trait::async_trait] #[async_trait::async_trait]
impl UserRoleInterface for Store { impl UserRoleInterface for Store {
#[instrument(skip_all)] #[instrument(skip_all)]
async fn insert_user_role( async fn insert_user_role(
&self, &self,
user_role: storage::UserRoleNew, user_role: InsertUserRolePayload,
) -> CustomResult<storage::UserRole, errors::StorageError> { ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
let conn = connection::pg_connection_write(self).await?; let conn = connection::pg_connection_write(self).await?;
user_role
.insert(&conn) storage::UserRole::insert_multiple_user_roles(&conn, user_role.convert_to_vec())
.await .await
.map_err(|error| report!(errors::StorageError::from(error))) .map_err(|error| report!(errors::StorageError::from(error)))
} }
@ -138,7 +153,6 @@ impl UserRoleInterface for Store {
.map_err(|error| report!(errors::StorageError::from(error))) .map_err(|error| report!(errors::StorageError::from(error)))
} }
#[instrument(skip_all)]
async fn list_user_roles_by_user_id_and_version( async fn list_user_roles_by_user_id_and_version(
&self, &self,
user_id: &str, user_id: &str,
@ -275,10 +289,15 @@ impl UserRoleInterface for Store {
impl UserRoleInterface for MockDb { impl UserRoleInterface for MockDb {
async fn insert_user_role( async fn insert_user_role(
&self, &self,
user_role: storage::UserRoleNew, user_role: InsertUserRolePayload,
) -> CustomResult<storage::UserRole, errors::StorageError> { ) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
let mut user_roles = self.user_roles.lock().await; let mut db_user_roles = self.user_roles.lock().await;
if user_roles
user_role
.convert_to_vec()
.into_iter()
.map(|user_role| {
if db_user_roles
.iter() .iter()
.any(|user_role_inner| user_role_inner.user_id == user_role.user_id) .any(|user_role_inner| user_role_inner.user_id == user_role.user_id)
{ {
@ -288,7 +307,7 @@ impl UserRoleInterface for MockDb {
})? })?
} }
let user_role = storage::UserRole { let user_role = storage::UserRole {
id: i32::try_from(user_roles.len()) id: i32::try_from(db_user_roles.len())
.change_context(errors::StorageError::MockDbError)?, .change_context(errors::StorageError::MockDbError)?,
user_id: user_role.user_id, user_id: user_role.user_id,
merchant_id: user_role.merchant_id, merchant_id: user_role.merchant_id,
@ -304,8 +323,10 @@ impl UserRoleInterface for MockDb {
entity_type: None, entity_type: None,
version: enums::UserRoleVersion::V1, version: enums::UserRoleVersion::V1,
}; };
user_roles.push(user_role.clone()); db_user_roles.push(user_role.clone());
Ok(user_role) Ok(user_role)
})
.collect::<Result<Vec<_>, _>>()
} }
async fn find_user_role_by_user_id( async fn find_user_role_by_user_id(

View File

@ -3,7 +3,7 @@ use std::{collections::HashSet, ops, str::FromStr};
use api_models::{ use api_models::{
admin as admin_api, organization as api_org, user as user_api, user_role as user_role_api, admin as admin_api, organization as api_org, user as user_api, user_role as user_role_api,
}; };
use common_enums::TokenPurpose; use common_enums::{EntityType, TokenPurpose};
use common_utils::{ use common_utils::{
crypto::Encryptable, errors::CustomResult, id_type, new_type::MerchantName, pii, type_name, crypto::Encryptable, errors::CustomResult, id_type, new_type::MerchantName, pii, type_name,
types::keymanager::Identifier, types::keymanager::Identifier,
@ -19,6 +19,7 @@ use masking::{ExposeInterface, PeekInterface, Secret};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rand::distributions::{Alphanumeric, DistString}; use rand::distributions::{Alphanumeric, DistString};
use router_env::env; use router_env::env;
use time::PrimitiveDateTime;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
#[cfg(feature = "keymanager_create")] #[cfg(feature = "keymanager_create")]
use {base64::Engine, common_utils::types::keymanager::EncryptionTransferRequest}; use {base64::Engine, common_utils::types::keymanager::EncryptionTransferRequest};
@ -29,9 +30,13 @@ use crate::{
admin, admin,
errors::{self, UserErrors, UserResult}, errors::{self, UserErrors, UserResult},
}, },
db::GlobalStorageInterface, db::{user_role::InsertUserRolePayload, GlobalStorageInterface},
routes::SessionState, routes::SessionState,
services::{self, authentication as auth, authentication::UserFromToken, authorization::info}, services::{
self,
authentication::{self as auth, UserFromToken},
authorization::info,
},
types::transformers::ForeignFrom, types::transformers::ForeignFrom,
utils::{self, user::password}, utils::{self, user::password},
}; };
@ -638,19 +643,15 @@ impl NewUser {
created_user created_user
} }
pub async fn insert_user_role_in_db( pub fn get_no_level_user_role(
self, self,
state: SessionState,
role_id: String, role_id: String,
user_status: UserStatus, user_status: UserStatus,
) -> UserResult<UserRole> { ) -> NewUserRole<NoLevel> {
let now = common_utils::date_time::now(); let now = common_utils::date_time::now();
let user_id = self.get_user_id(); let user_id = self.get_user_id();
state NewUserRole {
.store
.insert_user_role(UserRoleNew {
merchant_id: Some(self.get_new_merchant().get_merchant_id()),
status: user_status, status: user_status,
created_by: user_id.clone(), created_by: user_id.clone(),
last_modified_by: user_id.clone(), last_modified_by: user_id.clone(),
@ -658,18 +659,35 @@ impl NewUser {
role_id, role_id,
created_at: now, created_at: now,
last_modified: now, last_modified: now,
org_id: Some( entity: NoLevel,
self.get_new_merchant() }
}
pub async fn insert_org_level_user_role_in_db(
self,
state: SessionState,
role_id: String,
user_status: UserStatus,
version: Option<UserRoleVersion>,
) -> UserResult<UserRole> {
let org_id = self
.get_new_merchant()
.get_new_organization() .get_new_organization()
.get_organization_id(), .get_organization_id();
), let merchant_id = self.get_new_merchant().get_merchant_id();
profile_id: None,
entity_id: None, let org_user_role = self
entity_type: None, .get_no_level_user_role(role_id, user_status)
version: UserRoleVersion::V1, .add_entity(OrganizationLevel {
}) org_id,
.await merchant_id,
.change_context(UserErrors::InternalServerError) });
match version {
Some(UserRoleVersion::V1) => org_user_role.insert_in_v1(&state).await,
Some(UserRoleVersion::V2) => org_user_role.insert_in_v2(&state).await,
None => org_user_role.insert_in_v1_and_v2(&state).await,
}
} }
} }
@ -1282,3 +1300,252 @@ impl RecoveryCodes {
self.0 self.0
} }
} }
// This is for easier construction
#[derive(Clone)]
pub struct NoLevel;
#[derive(Clone)]
pub struct OrganizationLevel {
pub org_id: id_type::OrganizationId,
// Keeping this to allow insertion of org_admins in V1
pub merchant_id: id_type::MerchantId,
}
#[derive(Clone)]
pub struct MerchantLevel {
pub org_id: id_type::OrganizationId,
pub merchant_id: id_type::MerchantId,
}
#[derive(Clone)]
pub struct ProfileLevel {
pub org_id: id_type::OrganizationId,
pub merchant_id: id_type::MerchantId,
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,
pub role_id: String,
pub status: UserStatus,
pub created_by: String,
pub last_modified_by: String,
pub created_at: PrimitiveDateTime,
pub last_modified: PrimitiveDateTime,
pub entity: E,
}
impl NewUserRole<NoLevel> {
pub fn add_entity<T>(self, entity: T) -> NewUserRole<T>
where
T: Clone,
{
NewUserRole {
entity,
user_id: self.user_id,
role_id: self.role_id,
status: self.status,
created_by: self.created_by,
last_modified_by: self.last_modified_by,
created_at: self.created_at,
last_modified: self.last_modified,
}
}
}
pub struct EntityInfo {
org_id: id_type::OrganizationId,
merchant_id: Option<id_type::MerchantId>,
profile_id: Option<id_type::ProfileId>,
entity_id: String,
entity_type: EntityType,
}
impl<E> NewUserRole<E>
where
E: Clone,
{
fn convert_to_new_v1_role(
self,
org_id: id_type::OrganizationId,
merchant_id: id_type::MerchantId,
) -> UserRoleNew {
UserRoleNew {
user_id: self.user_id,
role_id: self.role_id,
status: self.status,
created_by: self.created_by,
last_modified_by: self.last_modified_by,
created_at: self.created_at,
last_modified: self.last_modified,
org_id: Some(org_id),
merchant_id: Some(merchant_id),
profile_id: None,
entity_id: None,
entity_type: None,
version: UserRoleVersion::V1,
}
}
fn convert_to_new_v2_role(self, entity: EntityInfo) -> UserRoleNew {
UserRoleNew {
user_id: self.user_id,
role_id: self.role_id,
status: self.status,
created_by: self.created_by,
last_modified_by: self.last_modified_by,
created_at: self.created_at,
last_modified: self.last_modified,
org_id: Some(entity.org_id),
merchant_id: entity.merchant_id,
profile_id: entity.profile_id,
entity_id: Some(entity.entity_id),
entity_type: Some(entity.entity_type),
version: UserRoleVersion::V2,
}
}
async fn insert_v1_and_v2_in_db_and_get_v1(
state: &SessionState,
v1_role: UserRoleNew,
v2_role: UserRoleNew,
) -> UserResult<UserRole> {
let inserted_roles = state
.store
.insert_user_role(InsertUserRolePayload::V1AndV2(Box::new([v1_role, v2_role])))
.await
.change_context(UserErrors::InternalServerError)?;
// Returning v1 role so other code which was not migrated doesn't break
inserted_roles
.into_iter()
.find(|role| role.version == UserRoleVersion::V1)
.ok_or(report!(UserErrors::InternalServerError))
}
}
impl NewUserRole<OrganizationLevel> {
pub async fn insert_in_v1(self, state: &SessionState) -> UserResult<UserRole> {
let entity = self.entity.clone();
let new_v1_role = self
.clone()
.convert_to_new_v1_role(entity.org_id.clone(), entity.merchant_id.clone());
state
.store
.insert_user_role(InsertUserRolePayload::OnlyV1(new_v1_role))
.await
.change_context(UserErrors::InternalServerError)?
.pop()
.ok_or(report!(UserErrors::InternalServerError))
}
pub async fn insert_in_v2(self, state: &SessionState) -> UserResult<UserRole> {
let entity = self.entity.clone();
let new_v2_role = self.convert_to_new_v2_role(EntityInfo {
org_id: entity.org_id.clone(),
merchant_id: None,
profile_id: None,
entity_id: entity.org_id.get_string_repr().to_owned(),
entity_type: EntityType::Organization,
});
state
.store
.insert_user_role(InsertUserRolePayload::OnlyV2(new_v2_role))
.await
.change_context(UserErrors::InternalServerError)?
.pop()
.ok_or(report!(UserErrors::InternalServerError))
}
pub async fn insert_in_v1_and_v2(self, state: &SessionState) -> UserResult<UserRole> {
let entity = self.entity.clone();
let new_v1_role = self
.clone()
.convert_to_new_v1_role(entity.org_id.clone(), entity.merchant_id.clone());
let new_v2_role = self.clone().convert_to_new_v2_role(EntityInfo {
org_id: entity.org_id.clone(),
merchant_id: None,
profile_id: None,
entity_id: entity.org_id.get_string_repr().to_owned(),
entity_type: EntityType::Organization,
});
Self::insert_v1_and_v2_in_db_and_get_v1(state, new_v1_role, new_v2_role).await
}
}
impl NewUserRole<MerchantLevel> {
pub async fn insert_in_v1_and_v2(self, state: &SessionState) -> UserResult<UserRole> {
let entity = self.entity.clone();
let new_v1_role = self
.clone()
.convert_to_new_v1_role(entity.org_id.clone(), entity.merchant_id.clone());
let new_v2_role = self.clone().convert_to_new_v2_role(EntityInfo {
org_id: entity.org_id.clone(),
merchant_id: Some(entity.merchant_id.clone()),
profile_id: None,
entity_id: entity.merchant_id.get_string_repr().to_owned(),
entity_type: EntityType::Merchant,
});
Self::insert_v1_and_v2_in_db_and_get_v1(state, new_v1_role, new_v2_role).await
}
}
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_v1(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();
let new_v2_role = self.convert_to_new_v2_role(EntityInfo {
org_id: entity.org_id.clone(),
merchant_id: Some(entity.merchant_id.clone()),
profile_id: Some(entity.profile_id.clone()),
entity_id: entity.profile_id.get_string_repr().to_owned(),
entity_type: EntityType::Profile,
});
state
.store
.insert_user_role(InsertUserRolePayload::OnlyV2(new_v2_role))
.await
.change_context(UserErrors::InternalServerError)?
.pop()
.ok_or(report!(UserErrors::InternalServerError))
}
}

View File

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
ALTER TABLE user_roles ADD CONSTRAINT user_merchant_unique UNIQUE (user_id, merchant_id);

View File

@ -0,0 +1,2 @@
-- Your SQL goes here
ALTER TABLE user_roles DROP CONSTRAINT user_merchant_unique;