mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-03 05:17:02 +08:00
feat(user): setup roles table with queries (#3691)
This commit is contained in:
@ -17,7 +17,8 @@ pub mod diesel_exports {
|
||||
DbProcessTrackerStatus as ProcessTrackerStatus, DbReconStatus as ReconStatus,
|
||||
DbRefundStatus as RefundStatus, DbRefundType as RefundType,
|
||||
DbRequestIncrementalAuthorization as RequestIncrementalAuthorization,
|
||||
DbRoutingAlgorithmKind as RoutingAlgorithmKind, DbUserStatus as UserStatus,
|
||||
DbRoleScope as RoleScope, DbRoutingAlgorithmKind as RoutingAlgorithmKind,
|
||||
DbUserStatus as UserStatus,
|
||||
};
|
||||
}
|
||||
pub use common_enums::*;
|
||||
@ -500,3 +501,53 @@ pub enum DashboardMetadata {
|
||||
IsMultipleConfiguration,
|
||||
IsChangePasswordRequired,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
frunk::LabelledGeneric,
|
||||
)]
|
||||
#[diesel_enum(storage_type = "db_enum")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum RoleScope {
|
||||
Merchant,
|
||||
Organization,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
PartialEq,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
strum::Display,
|
||||
strum::EnumString,
|
||||
frunk::LabelledGeneric,
|
||||
)]
|
||||
#[diesel_enum(storage_type = "text")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum PermissionGroup {
|
||||
OperationsView,
|
||||
OperationsManage,
|
||||
ConnectorsView,
|
||||
ConnectorsManage,
|
||||
WorkflowsView,
|
||||
WorkflowsManage,
|
||||
AnalyticsView,
|
||||
UsersView,
|
||||
UsersManage,
|
||||
MerchantDetailsView,
|
||||
MerchantDetailsManage,
|
||||
OrganizationManage,
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@ pub mod process_tracker;
|
||||
pub mod query;
|
||||
pub mod refund;
|
||||
pub mod reverse_lookup;
|
||||
pub mod role;
|
||||
pub mod routing_algorithm;
|
||||
#[allow(unused_qualifications)]
|
||||
pub mod schema;
|
||||
|
||||
@ -32,6 +32,7 @@ pub mod payouts;
|
||||
pub mod process_tracker;
|
||||
pub mod refund;
|
||||
pub mod reverse_lookup;
|
||||
pub mod role;
|
||||
pub mod routing_algorithm;
|
||||
pub mod user;
|
||||
pub mod user_role;
|
||||
|
||||
68
crates/diesel_models/src/query/role.rs
Normal file
68
crates/diesel_models/src/query/role.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};
|
||||
use router_env::tracing::{self, instrument};
|
||||
|
||||
use crate::{
|
||||
enums::RoleScope, query::generics, role::*, schema::roles::dsl, PgPooledConn, StorageResult,
|
||||
};
|
||||
|
||||
impl RoleNew {
|
||||
#[instrument(skip(conn))]
|
||||
pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Role> {
|
||||
generics::generic_insert(conn, self).await
|
||||
}
|
||||
}
|
||||
|
||||
impl Role {
|
||||
pub async fn find_by_role_id(conn: &PgPooledConn, role_id: &str) -> StorageResult<Self> {
|
||||
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
||||
conn,
|
||||
dsl::role_id.eq(role_id.to_owned()),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_by_role_id(
|
||||
conn: &PgPooledConn,
|
||||
role_id: &str,
|
||||
role_update: RoleUpdate,
|
||||
) -> StorageResult<Self> {
|
||||
generics::generic_update_with_unique_predicate_get_result::<
|
||||
<Self as HasTable>::Table,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
conn,
|
||||
dsl::role_id.eq(role_id.to_owned()),
|
||||
RoleUpdateInternal::from(role_update),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_by_role_id(conn: &PgPooledConn, role_id: &str) -> StorageResult<Self> {
|
||||
generics::generic_delete_one_with_result::<<Self as HasTable>::Table, _, _>(
|
||||
conn,
|
||||
dsl::role_id.eq(role_id.to_owned()),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_roles(
|
||||
conn: &PgPooledConn,
|
||||
merchant_id: &str,
|
||||
org_id: &str,
|
||||
) -> StorageResult<Vec<Self>> {
|
||||
let predicate = dsl::merchant_id.eq(merchant_id.to_owned()).or(dsl::org_id
|
||||
.eq(org_id.to_owned())
|
||||
.and(dsl::scope.eq(RoleScope::Organization)));
|
||||
|
||||
generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
|
||||
conn,
|
||||
predicate,
|
||||
None,
|
||||
None,
|
||||
Some(dsl::last_modified_at.asc()),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
83
crates/diesel_models/src/role.rs
Normal file
83
crates/diesel_models/src/role.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
|
||||
use time::PrimitiveDateTime;
|
||||
|
||||
use crate::{enums, schema::roles};
|
||||
|
||||
#[derive(Clone, Debug, Identifiable, Queryable)]
|
||||
#[diesel(table_name = roles)]
|
||||
pub struct Role {
|
||||
pub id: i32,
|
||||
pub role_name: String,
|
||||
pub role_id: String,
|
||||
pub merchant_id: String,
|
||||
pub org_id: String,
|
||||
#[diesel(deserialize_as = super::DieselArray<enums::PermissionGroup>)]
|
||||
pub groups: Vec<enums::PermissionGroup>,
|
||||
pub scope: enums::RoleScope,
|
||||
pub created_at: PrimitiveDateTime,
|
||||
pub created_by: String,
|
||||
pub last_modified_at: PrimitiveDateTime,
|
||||
pub last_modified_by: String,
|
||||
}
|
||||
|
||||
#[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
|
||||
#[diesel(table_name = roles)]
|
||||
pub struct RoleNew {
|
||||
pub role_name: String,
|
||||
pub role_id: String,
|
||||
pub merchant_id: String,
|
||||
pub org_id: String,
|
||||
#[diesel(deserialize_as = super::DieselArray<enums::PermissionGroup>)]
|
||||
pub groups: Vec<enums::PermissionGroup>,
|
||||
pub scope: enums::RoleScope,
|
||||
pub created_at: PrimitiveDateTime,
|
||||
pub created_by: String,
|
||||
pub last_modified_at: PrimitiveDateTime,
|
||||
pub last_modified_by: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)]
|
||||
#[diesel(table_name = roles)]
|
||||
pub struct RoleUpdateInternal {
|
||||
groups: Option<Vec<enums::PermissionGroup>>,
|
||||
role_name: Option<String>,
|
||||
last_modified_by: String,
|
||||
last_modified_at: PrimitiveDateTime,
|
||||
}
|
||||
|
||||
pub enum RoleUpdate {
|
||||
UpdateGroup {
|
||||
groups: Vec<enums::PermissionGroup>,
|
||||
last_modified_by: String,
|
||||
},
|
||||
UpdateRoleName {
|
||||
role_name: String,
|
||||
last_modified_by: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<RoleUpdate> for RoleUpdateInternal {
|
||||
fn from(value: RoleUpdate) -> Self {
|
||||
let last_modified_at = common_utils::date_time::now();
|
||||
match value {
|
||||
RoleUpdate::UpdateGroup {
|
||||
groups,
|
||||
last_modified_by,
|
||||
} => Self {
|
||||
groups: Some(groups),
|
||||
role_name: None,
|
||||
last_modified_at,
|
||||
last_modified_by,
|
||||
},
|
||||
RoleUpdate::UpdateRoleName {
|
||||
role_name,
|
||||
last_modified_by,
|
||||
} => Self {
|
||||
groups: None,
|
||||
role_name: Some(role_name),
|
||||
last_modified_at,
|
||||
last_modified_by,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -994,6 +994,31 @@ diesel::table! {
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
use diesel::sql_types::*;
|
||||
use crate::enums::diesel_exports::*;
|
||||
|
||||
roles (id) {
|
||||
id -> Int4,
|
||||
#[max_length = 64]
|
||||
role_name -> Varchar,
|
||||
#[max_length = 64]
|
||||
role_id -> Varchar,
|
||||
#[max_length = 64]
|
||||
merchant_id -> Varchar,
|
||||
#[max_length = 64]
|
||||
org_id -> Varchar,
|
||||
groups -> Array<Nullable<Text>>,
|
||||
scope -> RoleScope,
|
||||
created_at -> Timestamp,
|
||||
#[max_length = 64]
|
||||
created_by -> Varchar,
|
||||
last_modified_at -> Timestamp,
|
||||
#[max_length = 64]
|
||||
last_modified_by -> Varchar,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
use diesel::sql_types::*;
|
||||
use crate::enums::diesel_exports::*;
|
||||
@ -1095,6 +1120,7 @@ diesel::allow_tables_to_appear_in_same_query!(
|
||||
process_tracker,
|
||||
refund,
|
||||
reverse_lookup,
|
||||
roles,
|
||||
routing_algorithm,
|
||||
user_roles,
|
||||
users,
|
||||
|
||||
@ -31,6 +31,7 @@ pub mod payout_attempt;
|
||||
pub mod payouts;
|
||||
pub mod refund;
|
||||
pub mod reverse_lookup;
|
||||
pub mod role;
|
||||
pub mod routing_algorithm;
|
||||
pub mod user;
|
||||
pub mod user_role;
|
||||
@ -111,6 +112,7 @@ pub trait StorageInterface:
|
||||
+ authorization::AuthorizationInterface
|
||||
+ user::sample_data::BatchSampleDataInterface
|
||||
+ health_check::HealthCheckDbInterface
|
||||
+ role::RoleInterface
|
||||
+ 'static
|
||||
{
|
||||
fn get_scheduler_db(&self) -> Box<dyn scheduler::SchedulerInterface>;
|
||||
|
||||
@ -24,6 +24,7 @@ use time::PrimitiveDateTime;
|
||||
|
||||
use super::{
|
||||
dashboard_metadata::DashboardMetadataInterface,
|
||||
role::RoleInterface,
|
||||
user::{sample_data::BatchSampleDataInterface, UserInterface},
|
||||
user_role::UserRoleInterface,
|
||||
};
|
||||
@ -2256,3 +2257,45 @@ impl HealthCheckDbInterface for KafkaStore {
|
||||
self.diesel_store.health_check_db().await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl RoleInterface for KafkaStore {
|
||||
async fn insert_role(
|
||||
&self,
|
||||
role: storage::RoleNew,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
self.diesel_store.insert_role(role).await
|
||||
}
|
||||
|
||||
async fn find_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
self.diesel_store.find_role_by_role_id(role_id).await
|
||||
}
|
||||
|
||||
async fn update_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
role_update: storage::RoleUpdate,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.update_role_by_role_id(role_id, role_update)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delete_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
self.diesel_store.delete_role_by_role_id(role_id).await
|
||||
}
|
||||
|
||||
async fn list_all_roles(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
org_id: &str,
|
||||
) -> CustomResult<Vec<storage::Role>, errors::StorageError> {
|
||||
self.diesel_store.list_all_roles(merchant_id, org_id).await
|
||||
}
|
||||
}
|
||||
|
||||
236
crates/router/src/db/role.rs
Normal file
236
crates/router/src/db/role.rs
Normal file
@ -0,0 +1,236 @@
|
||||
use diesel_models::role as storage;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
|
||||
use super::MockDb;
|
||||
use crate::{
|
||||
connection,
|
||||
core::errors::{self, CustomResult},
|
||||
services::Store,
|
||||
};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait RoleInterface {
|
||||
async fn insert_role(
|
||||
&self,
|
||||
role: storage::RoleNew,
|
||||
) -> CustomResult<storage::Role, errors::StorageError>;
|
||||
|
||||
async fn find_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> CustomResult<storage::Role, errors::StorageError>;
|
||||
|
||||
async fn update_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
role_update: storage::RoleUpdate,
|
||||
) -> CustomResult<storage::Role, errors::StorageError>;
|
||||
|
||||
async fn delete_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> CustomResult<storage::Role, errors::StorageError>;
|
||||
|
||||
async fn list_all_roles(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
org_id: &str,
|
||||
) -> CustomResult<Vec<storage::Role>, errors::StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl RoleInterface for Store {
|
||||
async fn insert_role(
|
||||
&self,
|
||||
role: storage::RoleNew,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
role.insert(&conn).await.map_err(Into::into).into_report()
|
||||
}
|
||||
|
||||
async fn find_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::Role::find_by_role_id(&conn, role_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn update_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
role_update: storage::RoleUpdate,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::Role::update_by_role_id(&conn, role_id, role_update)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn delete_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::Role::delete_by_role_id(&conn, role_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn list_all_roles(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
org_id: &str,
|
||||
) -> CustomResult<Vec<storage::Role>, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::Role::list_roles(&conn, merchant_id, org_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl RoleInterface for MockDb {
|
||||
async fn insert_role(
|
||||
&self,
|
||||
role: storage::RoleNew,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
let mut roles = self.roles.lock().await;
|
||||
if roles
|
||||
.iter()
|
||||
.any(|role_inner| role_inner.role_id == role.role_id)
|
||||
{
|
||||
Err(errors::StorageError::DuplicateValue {
|
||||
entity: "role_id",
|
||||
key: None,
|
||||
})?
|
||||
}
|
||||
let role = storage::Role {
|
||||
id: roles
|
||||
.len()
|
||||
.try_into()
|
||||
.into_report()
|
||||
.change_context(errors::StorageError::MockDbError)?,
|
||||
role_name: role.role_name,
|
||||
role_id: role.role_id,
|
||||
merchant_id: role.merchant_id,
|
||||
org_id: role.org_id,
|
||||
groups: role.groups,
|
||||
scope: role.scope,
|
||||
created_by: role.created_by,
|
||||
created_at: role.created_at,
|
||||
last_modified_at: role.last_modified_at,
|
||||
last_modified_by: role.last_modified_by,
|
||||
};
|
||||
roles.push(role.clone());
|
||||
Ok(role)
|
||||
}
|
||||
|
||||
async fn find_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
let roles = self.roles.lock().await;
|
||||
roles
|
||||
.iter()
|
||||
.find(|role| role.role_id == role_id)
|
||||
.cloned()
|
||||
.ok_or(
|
||||
errors::StorageError::ValueNotFound(format!(
|
||||
"No role available role_id = {role_id}"
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn update_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
role_update: storage::RoleUpdate,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
let mut roles = self.roles.lock().await;
|
||||
let last_modified_at = common_utils::date_time::now();
|
||||
|
||||
roles
|
||||
.iter_mut()
|
||||
.find(|role| role.role_id == role_id)
|
||||
.map(|role| {
|
||||
*role = match role_update {
|
||||
storage::RoleUpdate::UpdateGroup {
|
||||
groups,
|
||||
last_modified_by,
|
||||
} => storage::Role {
|
||||
groups,
|
||||
last_modified_by,
|
||||
last_modified_at,
|
||||
..role.to_owned()
|
||||
},
|
||||
storage::RoleUpdate::UpdateRoleName {
|
||||
role_name,
|
||||
last_modified_by,
|
||||
} => storage::Role {
|
||||
role_name,
|
||||
last_modified_at,
|
||||
last_modified_by,
|
||||
..role.to_owned()
|
||||
},
|
||||
};
|
||||
role.to_owned()
|
||||
})
|
||||
.ok_or(
|
||||
errors::StorageError::ValueNotFound(format!(
|
||||
"No role available for role_id = {role_id}"
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn delete_role_by_role_id(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> CustomResult<storage::Role, errors::StorageError> {
|
||||
let mut roles = self.roles.lock().await;
|
||||
let role_index = roles
|
||||
.iter()
|
||||
.position(|role| role.role_id == role_id)
|
||||
.ok_or(errors::StorageError::ValueNotFound(format!(
|
||||
"No role available for role_id = {role_id}"
|
||||
)))?;
|
||||
|
||||
Ok(roles.remove(role_index))
|
||||
}
|
||||
|
||||
async fn list_all_roles(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
org_id: &str,
|
||||
) -> CustomResult<Vec<storage::Role>, errors::StorageError> {
|
||||
let roles = self.roles.lock().await;
|
||||
|
||||
let roles_list: Vec<_> = roles
|
||||
.iter()
|
||||
.filter(|role| {
|
||||
role.merchant_id == merchant_id
|
||||
|| (role.org_id == org_id
|
||||
&& role.scope == diesel_models::enums::RoleScope::Organization)
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if roles_list.is_empty() {
|
||||
return Err(errors::StorageError::ValueNotFound(format!(
|
||||
"No role found for merchant id = {} and org_id = {}",
|
||||
merchant_id, org_id
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(roles_list)
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,7 @@ pub mod payout_attempt;
|
||||
pub mod payouts;
|
||||
pub mod refund;
|
||||
pub mod reverse_lookup;
|
||||
pub mod role;
|
||||
pub mod routing_algorithm;
|
||||
pub mod user;
|
||||
pub mod user_role;
|
||||
@ -51,7 +52,8 @@ pub use self::{
|
||||
dashboard_metadata::*, dispute::*, ephemeral_key::*, events::*, file::*, fraud_check::*,
|
||||
gsm::*, locker_mock_up::*, mandate::*, merchant_account::*, merchant_connector_account::*,
|
||||
merchant_key_store::*, payment_link::*, payment_method::*, payout_attempt::*, payouts::*,
|
||||
process_tracker::*, refund::*, reverse_lookup::*, routing_algorithm::*, user::*, user_role::*,
|
||||
process_tracker::*, refund::*, reverse_lookup::*, role::*, routing_algorithm::*, user::*,
|
||||
user_role::*,
|
||||
};
|
||||
use crate::types::api::routing;
|
||||
|
||||
|
||||
1
crates/router/src/types/storage/role.rs
Normal file
1
crates/router/src/types/storage/role.rs
Normal file
@ -0,0 +1 @@
|
||||
pub use diesel_models::role::*;
|
||||
@ -45,6 +45,7 @@ pub struct MockDb {
|
||||
pub user_roles: Arc<Mutex<Vec<store::user_role::UserRole>>>,
|
||||
pub authorizations: Arc<Mutex<Vec<store::authorization::Authorization>>>,
|
||||
pub dashboard_metadata: Arc<Mutex<Vec<store::user::dashboard_metadata::DashboardMetadata>>>,
|
||||
pub roles: Arc<Mutex<Vec<store::role::Role>>>,
|
||||
}
|
||||
|
||||
impl MockDb {
|
||||
@ -82,6 +83,7 @@ impl MockDb {
|
||||
user_roles: Default::default(),
|
||||
authorizations: Default::default(),
|
||||
dashboard_metadata: Default::default(),
|
||||
roles: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
6
migrations/2024-02-14-092225_create_roles_table/down.sql
Normal file
6
migrations/2024-02-14-092225_create_roles_table/down.sql
Normal file
@ -0,0 +1,6 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
DROP INDEX IF EXISTS role_id_index;
|
||||
DROP INDEX IF EXISTS roles_merchant_org_index;
|
||||
|
||||
DROP TABLE IF EXISTS roles;
|
||||
DROP TYPE "RoleScope";
|
||||
19
migrations/2024-02-14-092225_create_roles_table/up.sql
Normal file
19
migrations/2024-02-14-092225_create_roles_table/up.sql
Normal file
@ -0,0 +1,19 @@
|
||||
-- Your SQL goes here
|
||||
CREATE TYPE "RoleScope" AS ENUM ('merchant','organization');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
id SERIAL PRIMARY KEY,
|
||||
role_name VARCHAR(64) NOT NULL,
|
||||
role_id VARCHAR(64) NOT NULL UNIQUE,
|
||||
merchant_id VARCHAR(64) NOT NULL,
|
||||
org_id VARCHAR(64) NOT NULL,
|
||||
groups TEXT[] NOT NULL,
|
||||
scope "RoleScope" NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now(),
|
||||
created_by VARCHAR(64) NOT NULL,
|
||||
last_modified_at TIMESTAMP NOT NULL DEFAULT now(),
|
||||
last_modified_by VARCHAR(64) NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS role_id_index ON roles (role_id);
|
||||
CREATE INDEX roles_merchant_org_index ON roles (merchant_id, org_id);
|
||||
Reference in New Issue
Block a user