Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(users): add profile level custom role #6380

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions crates/api_models/src/user_role/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub struct CreateRoleRequest {
pub role_name: String,
pub groups: Vec<PermissionGroup>,
pub role_scope: RoleScope,
pub entity_type: Option<EntityType>,
}

#[derive(Debug, serde::Deserialize, serde::Serialize)]
Expand All @@ -21,6 +22,7 @@ pub struct RoleInfoWithGroupsResponse {
pub groups: Vec<PermissionGroup>,
pub role_name: String,
pub role_scope: RoleScope,
pub entity_type: Option<EntityType>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why entity_type is option here? We have it non option in DB, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because in the request its optional , so thought in response also it should be optional

}

#[derive(Debug, serde::Serialize)]
Expand Down
18 changes: 16 additions & 2 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2679,6 +2679,8 @@ pub enum TransactionType {
Debug,
Eq,
PartialEq,
Ord,
PartialOrd,
serde::Deserialize,
serde::Serialize,
strum::Display,
Expand All @@ -2688,8 +2690,19 @@ pub enum TransactionType {
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RoleScope {
Merchant,
Organization,
Organization = 2,
Merchant = 1,
Profile = 0,
}

impl From<RoleScope> for EntityType {
fn from(role_scope: RoleScope) -> Self {
match role_scope {
RoleScope::Organization => Self::Organization,
RoleScope::Merchant => Self::Merchant,
RoleScope::Profile => Self::Profile,
}
}
}

/// Indicates the transaction status
Expand Down Expand Up @@ -3132,6 +3145,7 @@ pub enum ApiVersion {
serde::Serialize,
strum::Display,
strum::EnumString,
strum::EnumIter,
ToSchema,
Hash,
)]
Expand Down
94 changes: 93 additions & 1 deletion crates/diesel_models/src/query/role.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use async_bb8_diesel::AsyncRunQueryDsl;
use common_enums::EntityType;
use common_utils::id_type;
use diesel::{
associations::HasTable, debug_query, pg::Pg, result::Error as DieselError,
Expand Down Expand Up @@ -106,7 +107,7 @@ impl Role {
conn: &PgPooledConn,
org_id: id_type::OrganizationId,
merchant_id: Option<id_type::MerchantId>,
entity_type: Option<common_enums::EntityType>,
entity_type: Option<EntityType>,
limit: Option<u32>,
) -> StorageResult<Vec<Self>> {
let mut query = <Self as HasTable>::table()
Expand All @@ -131,6 +132,97 @@ impl Role {

router_env::logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());

match generics::db_metrics::track_database_call::<Self, _, _>(
query.get_results_async(conn),
generics::db_metrics::DatabaseOperation::Filter,
)
.await
{
Ok(value) => Ok(value),
Err(err) => match err {
DieselError::NotFound => {
Err(report!(err)).change_context(errors::DatabaseError::NotFound)
}
_ => Err(report!(err)).change_context(errors::DatabaseError::Others),
},
}
}
pub async fn generic_list_roles_by_entity_type(
conn: &PgPooledConn,
entity_type: ListRolesByEntityPayload,
is_lineage_data_required: bool,
limit: Option<u32>,
) -> StorageResult<Vec<Self>> {
let mut query = <Self as HasTable>::table().into_boxed();

match entity_type {
ListRolesByEntityPayload::Organization(org_id) => {
let entity_in_vec = if is_lineage_data_required {
vec![
EntityType::Organization,
EntityType::Merchant,
EntityType::Profile,
]
} else {
vec![EntityType::Organization]
};
query = query
.filter(dsl::org_id.eq(org_id))
.filter(
dsl::scope
.eq(RoleScope::Organization)
.or(dsl::scope.eq(RoleScope::Merchant))
.or(dsl::scope.eq(RoleScope::Profile)),
)
.filter(dsl::entity_type.eq_any(entity_in_vec))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for clarity, In case when is_lineage data_required true case this query will return same result as we will getting without applying this eq_any filter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

}

ListRolesByEntityPayload::Merchant(org_id, merchant_id) => {
let entity_in_vec = if is_lineage_data_required {
vec![EntityType::Merchant, EntityType::Profile]
} else {
vec![EntityType::Merchant]
};
query = query
.filter(dsl::org_id.eq(org_id))
.filter(
dsl::scope
.eq(RoleScope::Organization)
.or(dsl::scope
.eq(RoleScope::Merchant)
.and(dsl::merchant_id.eq(merchant_id.clone())))
.or(dsl::scope
.eq(RoleScope::Profile)
.and(dsl::merchant_id.eq(merchant_id))),
)
.filter(dsl::entity_type.eq_any(entity_in_vec))
}

ListRolesByEntityPayload::Profile(org_id, merchant_id, profile_id) => {
let entity_in_vec = vec![EntityType::Profile];
query = query
.filter(dsl::org_id.eq(org_id))
.filter(
dsl::scope
.eq(RoleScope::Organization)
.or(dsl::scope
.eq(RoleScope::Merchant)
.and(dsl::merchant_id.eq(merchant_id.clone())))
.or(dsl::scope
.eq(RoleScope::Profile)
.and(dsl::merchant_id.eq(merchant_id))
.and(dsl::profile_id.eq(profile_id))),
)
.filter(dsl::entity_type.eq_any(entity_in_vec))
}
};

if let Some(limit) = limit {
query = query.limit(limit.into());
}

router_env::logger::debug!(query = %debug_query::<Pg,_>(&query).to_string());

match generics::db_metrics::track_database_call::<Self, _, _>(
query.get_results_async(conn),
generics::db_metrics::DatabaseOperation::Filter,
Expand Down
12 changes: 12 additions & 0 deletions crates/diesel_models/src/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub struct Role {
pub last_modified_at: PrimitiveDateTime,
pub last_modified_by: String,
pub entity_type: enums::EntityType,
pub profile_id: Option<id_type::ProfileId>,
}

#[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
Expand All @@ -36,6 +37,7 @@ pub struct RoleNew {
pub last_modified_at: PrimitiveDateTime,
pub last_modified_by: String,
pub entity_type: enums::EntityType,
pub profile_id: Option<id_type::ProfileId>,
}

#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
Expand Down Expand Up @@ -73,3 +75,13 @@ impl From<RoleUpdate> for RoleUpdateInternal {
}
}
}

pub enum ListRolesByEntityPayload {
Profile(
id_type::OrganizationId,
id_type::MerchantId,
id_type::ProfileId,
),
Merchant(id_type::OrganizationId, id_type::MerchantId),
Organization(id_type::OrganizationId),
}
2 changes: 2 additions & 0 deletions crates/diesel_models/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,8 @@ diesel::table! {
last_modified_by -> Varchar,
#[max_length = 64]
entity_type -> Varchar,
#[max_length = 64]
profile_id -> Nullable<Varchar>,
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/diesel_models/src/schema_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,8 @@ diesel::table! {
last_modified_by -> Varchar,
#[max_length = 64]
entity_type -> Varchar,
#[max_length = 64]
profile_id -> Nullable<Varchar>,
}
}

Expand Down
Loading
Loading