diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 03dbf539c3..a9dda658d4 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -1109,6 +1109,111 @@ ] } }, + "/organization": { + "post": { + "tags": [ + "Organization" + ], + "summary": "Organization - Create", + "description": "Organization - Create\n\nCreate a new organization", + "operationId": "Create an Organization", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationRequest" + }, + "examples": { + "Create an organization with organization_name": { + "value": { + "organization_name": "organization_abc" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Organization Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationResponse" + } + } + } + }, + "400": { + "description": "Invalid data" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, + "/organization/{organization_id}": { + "post": { + "tags": [ + "Organization" + ], + "summary": "Organization - Update", + "description": "Organization - Update\n\nCreate a new organization for .", + "operationId": "Create an Organization", + "parameters": [ + { + "name": "organization_id", + "in": "path", + "description": "The unique identifier for the Organization", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationRequest" + }, + "examples": { + "Update organization_name of the organization": { + "value": { + "organization_name": "organization_abcd" + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Organization Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationResponse" + } + } + } + }, + "400": { + "description": "Invalid data" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, "/accounts": { "post": { "tags": [ @@ -11312,7 +11417,10 @@ "organization_id": { "type": "string", "description": "The id of the organization to which the merchant belongs to, if not passed an organization is created", - "nullable": true + "example": "org_q98uSGAYbjEwqs0mJwnz", + "nullable": true, + "maxLength": 64, + "minLength": 1 }, "pm_collect_link_config": { "allOf": [ @@ -11529,7 +11637,10 @@ }, "organization_id": { "type": "string", - "description": "The organization id merchant is associated with" + "description": "The organization id merchant is associated with", + "example": "org_q98uSGAYbjEwqs0mJwnz", + "maxLength": 64, + "minLength": 1 }, "is_recon_enabled": { "type": "boolean", @@ -12917,6 +13028,33 @@ } } }, + "OrganizationRequest": { + "type": "object", + "properties": { + "organization_name": { + "type": "string", + "nullable": true + } + } + }, + "OrganizationResponse": { + "type": "object", + "required": [ + "organization_id" + ], + "properties": { + "organization_id": { + "type": "string", + "example": "org_q98uSGAYbjEwqs0mJwnz", + "maxLength": 64, + "minLength": 1 + }, + "organization_name": { + "type": "string", + "nullable": true + } + } + }, "OutgoingWebhook": { "type": "object", "required": [ diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 3e90a48ff0..fe60a2d445 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -108,7 +108,8 @@ pub struct MerchantAccountCreate { pub frm_routing_algorithm: Option, /// The id of the organization to which the merchant belongs to, if not passed an organization is created - pub organization_id: Option, + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")] + pub organization_id: Option, /// Default payment method collect link config #[schema(value_type = Option)] @@ -208,7 +209,8 @@ pub struct MerchantAccountCreate { pub metadata: Option, /// The id of the organization to which the merchant belongs to. Please use the organization endpoint to create an organization - pub organization_id: String, + #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")] + pub organization_id: id_type::OrganizationId, } #[cfg(all(feature = "v2", feature = "merchant_account_v2"))] @@ -414,7 +416,8 @@ pub struct MerchantAccountResponse { pub frm_routing_algorithm: Option, /// The organization id merchant is associated with - pub organization_id: String, + #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")] + pub organization_id: id_type::OrganizationId, /// A boolean value to indicate if the merchant has recon service is enabled or not, by default value is false pub is_recon_enabled: bool, @@ -456,7 +459,8 @@ pub struct MerchantAccountResponse { pub metadata: Option, /// The id of the organization which the merchant is associated with - pub organization_id: String, + #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")] + pub organization_id: id_type::OrganizationId, /// A boolean value to indicate if the merchant has recon service is enabled or not, by default value is false pub is_recon_enabled: bool, diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index 32cdd83eb9..8289a320d0 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -31,6 +31,7 @@ use crate::{ disputes::*, files::*, mandates::*, + organization::{OrganizationId, OrganizationRequest, OrganizationResponse}, payment_methods::*, payments::*, user::{UserKeyTransferRequest, UserTransferKeyResponse}, @@ -127,7 +128,10 @@ impl_api_event_type!( GetSearchRequestWithIndex, GetDisputeFilterRequest, DisputeFiltersResponse, - GetDisputeMetricRequest + GetDisputeMetricRequest, + OrganizationResponse, + OrganizationRequest, + OrganizationId ) ); diff --git a/crates/api_models/src/organization.rs b/crates/api_models/src/organization.rs index db4ae21a0d..6466e6cc87 100644 --- a/crates/api_models/src/organization.rs +++ b/crates/api_models/src/organization.rs @@ -1,13 +1,32 @@ +use common_utils::id_type; +use utoipa::ToSchema; pub struct OrganizationNew { - pub org_id: String, + pub org_id: id_type::OrganizationId, pub org_name: Option, } impl OrganizationNew { pub fn new(org_name: Option) -> Self { Self { - org_id: common_utils::generate_id_with_default_len("org"), + org_id: id_type::OrganizationId::default(), org_name, } } } + +#[derive(Clone, Debug, serde::Serialize)] +pub struct OrganizationId { + pub organization_id: id_type::OrganizationId, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)] +pub struct OrganizationRequest { + pub organization_name: Option, +} + +#[derive(Debug, serde::Serialize, Clone, ToSchema)] +pub struct OrganizationResponse { + #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")] + pub organization_id: id_type::OrganizationId, + pub organization_name: Option, +} diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 2f3d29c0b8..5bf855c490 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -1,5 +1,5 @@ use common_enums::{PermissionGroup, RoleScope, TokenPurpose}; -use common_utils::{crypto::OptionalEncryptableName, pii}; +use common_utils::{crypto::OptionalEncryptableName, id_type, pii}; use masking::Secret; use crate::user_role::UserStatus; @@ -164,7 +164,7 @@ pub struct GetUserDetailsResponse { // This field is added for audit/debug reasons #[serde(skip_serializing)] pub user_id: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, pub is_two_factor_auth_setup: bool, pub recovery_codes_left: Option, } @@ -206,7 +206,7 @@ pub struct UserMerchantAccount { pub is_active: bool, pub role_id: String, pub role_name: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, } #[cfg(feature = "recon")] diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs index 2e6764d355..100f7957f5 100644 --- a/crates/common_utils/src/id_type.rs +++ b/crates/common_utils/src/id_type.rs @@ -1,13 +1,11 @@ //! Common ID types //! The id type can be used to create specific id types with custom behaviour -use std::{ - borrow::Cow, - fmt::{Debug, Display}, -}; +use std::{borrow::Cow, fmt::Debug}; mod customer; mod merchant; +mod organization; pub use customer::CustomerId; use diesel::{ @@ -18,6 +16,7 @@ use diesel::{ sql_types, }; pub use merchant::MerchantId; +pub use organization::OrganizationId; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -37,7 +36,7 @@ fn get_invalid_input_character(input_string: Cow<'static, str>) -> Option .find(|char| !is_valid_id_character(char)) } -#[derive(Debug, PartialEq, Serialize, Clone, Eq)] +#[derive(Debug, PartialEq, Serialize, Clone, Eq, Hash)] /// A type for alphanumeric ids pub(crate) struct AlphaNumericId(String); @@ -83,7 +82,7 @@ impl AlphaNumericId { } /// A common type of id that can be used for reference ids with length constraint -#[derive(Debug, Clone, Serialize, PartialEq, Eq, AsExpression)] +#[derive(Debug, Clone, Serialize, PartialEq, Eq, AsExpression, Hash)] #[diesel(sql_type = sql_types::Text)] pub(crate) struct LengthId(AlphaNumericId); @@ -168,12 +167,6 @@ where } } -impl Display for LengthId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0 .0) - } -} - impl FromSql for LengthId where diff --git a/crates/common_utils/src/id_type/organization.rs b/crates/common_utils/src/id_type/organization.rs new file mode 100644 index 0000000000..cd2b6c202f --- /dev/null +++ b/crates/common_utils/src/id_type/organization.rs @@ -0,0 +1,106 @@ +use std::{borrow::Cow, fmt::Debug}; + +use diesel::{ + backend::Backend, + deserialize::FromSql, + expression::AsExpression, + serialize::{Output, ToSql}, + sql_types, Queryable, +}; +use error_stack::{Result, ResultExt}; +use serde::{Deserialize, Serialize}; + +use crate::{ + consts::{MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH}, + errors, generate_organization_id_of_default_length, + id_type::LengthId, +}; + +/// A type for customer_id that can be used for customer ids +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, AsExpression, Hash)] +#[diesel(sql_type = sql_types::Text)] +pub struct OrganizationId( + LengthId, +); + +impl Default for OrganizationId { + fn default() -> Self { + generate_organization_id_of_default_length() + } +} + +/// This is to display the `OrganizationId` as OrganizationId(abcd) +impl Debug for OrganizationId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("OrganizationId") + .field(&self.0 .0 .0) + .finish() + } +} + +impl Queryable for OrganizationId +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + + fn build(row: Self::Row) -> diesel::deserialize::Result { + Ok(row) + } +} + +impl OrganizationId { + pub(crate) fn new( + organization_id: LengthId< + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, + MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + >, + ) -> Self { + Self(organization_id) + } + + /// Get the string representation of customer id + pub fn get_string_repr(&self) -> &str { + &self.0 .0 .0 + } + + /// Create a Customer id from string + pub fn from(input_string: Cow<'static, str>) -> Result { + let organization_id = LengthId::from(input_string).change_context( + errors::ValidationError::IncorrectValueProvided { + field_name: "customer_id", + }, + )?; + + Ok(Self(organization_id)) + } +} + +impl masking::SerializableSecret for OrganizationId {} + +impl ToSql for OrganizationId +where + DB: Backend, + LengthId: + ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} + +impl FromSql for OrganizationId +where + DB: Backend, + LengthId: + FromSql, +{ + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + LengthId::< + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, + MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + >::from_sql(value) + .map(Self) + } +} diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index e93b0c66ce..e7a8076195 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -4,11 +4,6 @@ use masking::{PeekInterface, Secret}; -use crate::{ - consts::ID_LENGTH, - id_type::{CustomerId, LengthId}, -}; - pub mod access_token; pub mod consts; pub mod crypto; @@ -216,19 +211,24 @@ pub fn generate_id(length: usize, prefix: &str) -> String { /// Generate a ReferenceId with the default length with the given prefix fn generate_ref_id_with_default_length( prefix: &str, -) -> LengthId { - LengthId::::new(prefix) +) -> id_type::LengthId { + id_type::LengthId::::new(prefix) } /// Generate a customer id with default length, with prefix as `cus` -pub fn generate_customer_id_of_default_length() -> CustomerId { - CustomerId::new(generate_ref_id_with_default_length("cus")) +pub fn generate_customer_id_of_default_length() -> id_type::CustomerId { + id_type::CustomerId::new(generate_ref_id_with_default_length("cus")) +} + +/// Generate a organization id with default length, with prefix as `org` +pub fn generate_organization_id_of_default_length() -> id_type::OrganizationId { + id_type::OrganizationId::new(generate_ref_id_with_default_length("org")) } /// Generate a nanoid with the given prefix and a default length #[inline] pub fn generate_id_with_default_len(prefix: &str) -> String { - let len = ID_LENGTH; + let len: usize = consts::ID_LENGTH; format!("{}_{}", prefix, nanoid::nanoid!(len, &consts::ALPHABETS)) } @@ -278,7 +278,7 @@ mod nanoid_tests { #[test] fn test_generate_merchant_ref_id_with_default_length() { - let ref_id = LengthId::< + let ref_id = id_type::LengthId::< MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, >::from(generate_id_with_default_len("def").into()); diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index a8a2e7ce19..800e5636a9 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -1,4 +1,4 @@ -use common_utils::{encryption::Encryption, pii}; +use common_utils::{encryption::Encryption, id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use crate::enums as storage_enums; @@ -48,7 +48,7 @@ pub struct MerchantAccount { pub modified_at: time::PrimitiveDateTime, pub frm_routing_algorithm: Option, pub payout_routing_algorithm: Option, - pub organization_id: String, + pub organization_id: id_type::OrganizationId, pub is_recon_enabled: bool, pub default_profile: Option, pub recon_status: storage_enums::ReconStatus, @@ -90,7 +90,7 @@ pub struct MerchantAccount { pub modified_at: time::PrimitiveDateTime, pub frm_routing_algorithm: Option, pub payout_routing_algorithm: Option, - pub organization_id: String, + pub organization_id: id_type::OrganizationId, pub is_recon_enabled: bool, pub default_profile: Option, pub recon_status: storage_enums::ReconStatus, @@ -121,7 +121,7 @@ pub struct MerchantAccountNew { pub modified_at: time::PrimitiveDateTime, pub frm_routing_algorithm: Option, pub payout_routing_algorithm: Option, - pub organization_id: String, + pub organization_id: id_type::OrganizationId, pub is_recon_enabled: bool, pub default_profile: Option, pub recon_status: storage_enums::ReconStatus, @@ -151,7 +151,7 @@ pub struct MerchantAccountUpdateInternal { pub intent_fulfillment_time: Option, pub frm_routing_algorithm: Option, pub payout_routing_algorithm: Option, - pub organization_id: Option, + pub organization_id: Option, pub is_recon_enabled: bool, pub default_profile: Option>, pub recon_status: Option, diff --git a/crates/diesel_models/src/organization.rs b/crates/diesel_models/src/organization.rs index 5c2012a6d9..e6ffaaadab 100644 --- a/crates/diesel_models/src/organization.rs +++ b/crates/diesel_models/src/organization.rs @@ -1,18 +1,18 @@ +use common_utils::id_type; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use crate::schema::organization; - #[derive(Clone, Debug, Identifiable, Queryable, Selectable)] #[diesel(table_name = organization, primary_key(org_id), check_for_backend(diesel::pg::Pg))] pub struct Organization { - pub org_id: String, + pub org_id: id_type::OrganizationId, pub org_name: Option, } #[derive(Clone, Debug, Insertable)] #[diesel(table_name = organization, primary_key(org_id))] pub struct OrganizationNew { - pub org_id: String, + pub org_id: id_type::OrganizationId, pub org_name: Option, } diff --git a/crates/diesel_models/src/query/dashboard_metadata.rs b/crates/diesel_models/src/query/dashboard_metadata.rs index 725a95ffef..e180def2c0 100644 --- a/crates/diesel_models/src/query/dashboard_metadata.rs +++ b/crates/diesel_models/src/query/dashboard_metadata.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; use crate::{ @@ -22,7 +23,7 @@ impl DashboardMetadata { conn: &PgPooledConn, user_id: Option, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, data_key: enums::DashboardMetadata, dashboard_metadata_update: DashboardMetadataUpdate, ) -> StorageResult { @@ -62,7 +63,7 @@ impl DashboardMetadata { conn: &PgPooledConn, user_id: String, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, data_types: Vec, ) -> StorageResult> { let predicate = dsl::user_id @@ -84,7 +85,7 @@ impl DashboardMetadata { pub async fn find_merchant_scoped_dashboard_metadata( conn: &PgPooledConn, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, data_types: Vec, ) -> StorageResult> { let predicate = dsl::merchant_id diff --git a/crates/diesel_models/src/query/organization.rs b/crates/diesel_models/src/query/organization.rs index ea8698204d..671f742d88 100644 --- a/crates/diesel_models/src/query/organization.rs +++ b/crates/diesel_models/src/query/organization.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{associations::HasTable, ExpressionMethods}; use crate::{ @@ -11,14 +12,17 @@ impl OrganizationNew { } impl Organization { - pub async fn find_by_org_id(conn: &PgPooledConn, org_id: String) -> StorageResult { + pub async fn find_by_org_id( + conn: &PgPooledConn, + org_id: id_type::OrganizationId, + ) -> StorageResult { generics::generic_find_one::<::Table, _, _>(conn, dsl::org_id.eq(org_id)) .await } pub async fn update_by_org_id( conn: &PgPooledConn, - org_id: String, + org_id: id_type::OrganizationId, update: OrganizationUpdate, ) -> StorageResult { generics::generic_update_with_unique_predicate_get_result::< diff --git a/crates/diesel_models/src/query/role.rs b/crates/diesel_models/src/query/role.rs index 73b596171d..3b07afb7b8 100644 --- a/crates/diesel_models/src/query/role.rs +++ b/crates/diesel_models/src/query/role.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; use crate::{ @@ -23,7 +24,7 @@ impl Role { conn: &PgPooledConn, role_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> StorageResult { generics::generic_find_one::<::Table, _, _>( conn, @@ -65,7 +66,7 @@ impl Role { pub async fn list_roles( conn: &PgPooledConn, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> StorageResult> { let predicate = dsl::merchant_id.eq(merchant_id.to_owned()).or(dsl::org_id .eq(org_id.to_owned()) diff --git a/crates/diesel_models/src/query/user_role.rs b/crates/diesel_models/src/query/user_role.rs index 9e29b28e61..5dc5309fbe 100644 --- a/crates/diesel_models/src/query/user_role.rs +++ b/crates/diesel_models/src/query/user_role.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; use crate::{query::generics, schema::user_roles::dsl, user_role::*, PgPooledConn, StorageResult}; @@ -55,7 +56,7 @@ impl UserRole { pub async fn update_by_user_id_org_id( conn: &PgPooledConn, user_id: String, - org_id: String, + org_id: id_type::OrganizationId, update: UserRoleUpdate, ) -> StorageResult> { generics::generic_update_with_results::<::Table, _, _, _>( diff --git a/crates/diesel_models/src/role.rs b/crates/diesel_models/src/role.rs index b5b8062803..cf23e36d83 100644 --- a/crates/diesel_models/src/role.rs +++ b/crates/diesel_models/src/role.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; @@ -10,7 +11,7 @@ pub struct Role { pub role_name: String, pub role_id: String, pub merchant_id: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, #[diesel(deserialize_as = super::DieselArray)] pub groups: Vec, pub scope: enums::RoleScope, @@ -26,7 +27,7 @@ pub struct RoleNew { pub role_name: String, pub role_id: String, pub merchant_id: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, #[diesel(deserialize_as = super::DieselArray)] pub groups: Vec, pub scope: enums::RoleScope, diff --git a/crates/diesel_models/src/user/dashboard_metadata.rs b/crates/diesel_models/src/user/dashboard_metadata.rs index a01f88fcb1..78caa20a93 100644 --- a/crates/diesel_models/src/user/dashboard_metadata.rs +++ b/crates/diesel_models/src/user/dashboard_metadata.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{query_builder::AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; @@ -9,7 +10,7 @@ pub struct DashboardMetadata { pub id: i32, pub user_id: Option, pub merchant_id: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, pub data_key: enums::DashboardMetadata, pub data_value: serde_json::Value, pub created_by: String, @@ -25,7 +26,7 @@ pub struct DashboardMetadata { pub struct DashboardMetadataNew { pub user_id: Option, pub merchant_id: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, pub data_key: enums::DashboardMetadata, pub data_value: serde_json::Value, pub created_by: String, diff --git a/crates/diesel_models/src/user_role.rs b/crates/diesel_models/src/user_role.rs index ea8b626358..28cbac2f73 100644 --- a/crates/diesel_models/src/user_role.rs +++ b/crates/diesel_models/src/user_role.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; @@ -10,7 +11,7 @@ pub struct UserRole { pub user_id: String, pub merchant_id: String, pub role_id: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, pub status: enums::UserStatus, pub created_by: String, pub last_modified_by: String, @@ -24,7 +25,7 @@ pub struct UserRoleNew { pub user_id: String, pub merchant_id: String, pub role_id: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, pub status: enums::UserStatus, pub created_by: String, pub last_modified_by: String, diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index bf8a1b9caa..5ff3fec14c 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -1,3 +1,10 @@ +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_account_v2") +))] +use common_utils::id_type; +#[cfg(all(feature = "v2", feature = "merchant_account_v2"))] +use common_utils::id_type; use common_utils::{ crypto::{OptionalEncryptableName, OptionalEncryptableValue}, date_time, @@ -5,7 +12,7 @@ use common_utils::{ errors::{CustomResult, ValidationError}, ext_traits::ValueExt, pii, - types::keymanager::{self}, + types::keymanager, }; use diesel_models::{ enums::MerchantStorageScheme, merchant_account::MerchantAccountUpdateInternal, @@ -44,7 +51,7 @@ pub struct MerchantAccount { pub modified_at: time::PrimitiveDateTime, pub intent_fulfillment_time: Option, pub payout_routing_algorithm: Option, - pub organization_id: String, + pub organization_id: id_type::OrganizationId, pub is_recon_enabled: bool, pub default_profile: Option, pub recon_status: diesel_models::enums::ReconStatus, @@ -76,7 +83,7 @@ pub struct MerchantAccount { pub modified_at: time::PrimitiveDateTime, pub intent_fulfillment_time: Option, pub payout_routing_algorithm: Option, - pub organization_id: String, + pub organization_id: id_type::OrganizationId, pub is_recon_enabled: bool, pub default_profile: Option, pub recon_status: diesel_models::enums::ReconStatus, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index 2338f5edc1..2bf8f56260 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -1,10 +1,14 @@ use api_models::enums::Connector; use common_enums as storage_enums; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "payment_v2")))] +use common_utils::types::keymanager::KeyManagerState; +#[cfg(all(feature = "v2", feature = "payment_v2"))] +use common_utils::types::keymanager::KeyManagerState; use common_utils::{ encryption::Encryption, errors::{CustomResult, ValidationError}, pii, - types::{keymanager::KeyManagerState, MinorUnit}, + types::MinorUnit, }; use error_stack::ResultExt; use masking::PeekInterface; @@ -540,7 +544,6 @@ impl behaviour::Conversion for PaymentIntent { shipping_details: self.shipping_details.map(Encryption::from), }) } - async fn convert_back( state: &KeyManagerState, storage_model: Self::DstType, diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 233f43b084..4a7d471003 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -89,6 +89,11 @@ Never share your secret api keys. Keep them guarded and secure. routes::refunds::refunds_update, routes::refunds::refunds_list, + // Routes for Organization + routes::organization::organization_create, + routes::organization::organization_retrieve, + routes::organization::organization_update, + // Routes for merchant account routes::merchant_account::merchant_account_create, routes::merchant_account::retrieve_merchant_account, @@ -193,6 +198,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::refunds::RefundResponse, api_models::refunds::RefundStatus, api_models::refunds::RefundUpdateRequest, + api_models::organization::OrganizationRequest, + api_models::organization::OrganizationResponse, api_models::admin::MerchantAccountCreate, api_models::admin::MerchantAccountUpdate, api_models::admin::MerchantAccountDeleteResponse, diff --git a/crates/openapi/src/routes.rs b/crates/openapi/src/routes.rs index 1df4e4e4f8..406c88e2e3 100644 --- a/crates/openapi/src/routes.rs +++ b/crates/openapi/src/routes.rs @@ -9,6 +9,7 @@ pub mod gsm; pub mod mandates; pub mod merchant_account; pub mod merchant_connector_account; +pub mod organization; pub mod payment_link; pub mod payment_method; pub mod payments; @@ -19,6 +20,6 @@ pub mod routing; pub mod webhook_events; pub use self::{ - customers::*, mandates::*, merchant_account::*, merchant_connector_account::*, + customers::*, mandates::*, merchant_account::*, merchant_connector_account::*, organization::*, payment_method::*, payments::*, poll::*, refunds::*, routing::*, webhook_events::*, }; diff --git a/crates/openapi/src/routes/organization.rs b/crates/openapi/src/routes/organization.rs new file mode 100644 index 0000000000..893fff7315 --- /dev/null +++ b/crates/openapi/src/routes/organization.rs @@ -0,0 +1,69 @@ +/// Organization - Create +/// +/// Create a new organization +#[utoipa::path( + post, + path = "/organization", + request_body( + content = OrganizationRequest, + examples( + ( + "Create an organization with organization_name" = ( + value = json!({"organization_name": "organization_abc"}) + ) + ), + ) + ), + responses( + (status = 200, description = "Organization Created", body =OrganizationResponse), + (status = 400, description = "Invalid data") + ), + tag = "Organization", + operation_id = "Create an Organization", + security(("admin_api_key" = [])) +)] +pub async fn organization_create() {} + +/// Organization - Retrieve +/// +/// Retrieve an existing organization +#[utoipa::path( + post, + path = "/organization/{organization_id}", + params (("organization_id" = String, Path, description = "The unique identifier for the Organization")), + responses( + (status = 200, description = "Organization Created", body =OrganizationResponse), + (status = 400, description = "Invalid data") + ), + tag = "Organization", + operation_id = "Create an Organization", + security(("admin_api_key" = [])) +)] +pub async fn organization_retrieve() {} + +/// Organization - Update +/// +/// Create a new organization for . +#[utoipa::path( + post, + path = "/organization/{organization_id}", + request_body( + content = OrganizationRequest, + examples( + ( + "Update organization_name of the organization" = ( + value = json!({"organization_name": "organization_abcd"}) + ) + ), + ) + ), + params (("organization_id" = String, Path, description = "The unique identifier for the Organization")), + responses( + (status = 200, description = "Organization Created", body =OrganizationResponse), + (status = 400, description = "Invalid data") + ), + tag = "Organization", + operation_id = "Create an Organization", + security(("admin_api_key" = [])) +)] +pub async fn organization_update() {} diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index ca73a07de3..4a816a95ab 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -105,6 +105,73 @@ fn add_publishable_key_to_decision_service( ); } +#[cfg(feature = "olap")] +pub async fn create_organization( + state: SessionState, + req: api::OrganizationRequest, +) -> RouterResponse { + let db_organization = ForeignFrom::foreign_from(req); + state + .store + .insert_organization(db_organization) + .await + .to_duplicate_response(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error when creating organization") + .map(ForeignFrom::foreign_from) + .map(service_api::ApplicationResponse::Json) +} + +#[cfg(feature = "olap")] +pub async fn update_organization( + state: SessionState, + org_id: api::OrganizationId, + req: api::OrganizationRequest, +) -> RouterResponse { + let organization_update = diesel_models::organization::OrganizationUpdate::Update { + org_name: req.organization_name, + }; + state + .store + .update_organization_by_org_id(&org_id.organization_id, organization_update) + .await + .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { + message: "organization with the given id does not exist".to_string(), + }) + .attach_printable(format!( + "Failed to update organization with organization_id: {:?}", + org_id.organization_id + )) + .map(ForeignFrom::foreign_from) + .map(service_api::ApplicationResponse::Json) +} + +#[cfg(feature = "olap")] +pub async fn get_organization( + state: SessionState, + org_id: api::OrganizationId, +) -> RouterResponse { + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "merchant_account_v2"), + feature = "olap" + ))] + { + CreateOrValidateOrganization::new(Some(org_id.organization_id)) + .create_or_validate(state.store.as_ref()) + .await + .map(ForeignFrom::foreign_from) + .map(service_api::ApplicationResponse::Json) + } + #[cfg(all(feature = "v2", feature = "merchant_account_v2", feature = "olap"))] + { + CreateOrValidateOrganization::new(org_id.organization_id) + .create_or_validate(state.store.as_ref()) + .await + .map(ForeignFrom::foreign_from) + .map(service_api::ApplicationResponse::Json) + } +} + #[cfg(feature = "olap")] pub async fn create_merchant_account( state: SessionState, @@ -257,7 +324,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { ) .await?; - let organization_id = CreateOrValidateOrganization::new(self.organization_id) + let organization = CreateOrValidateOrganization::new(self.organization_id) .create_or_validate(db) .await?; @@ -316,7 +383,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { payout_routing_algorithm: self.payout_routing_algorithm, #[cfg(not(feature = "payouts"))] payout_routing_algorithm: None, - organization_id, + organization_id: organization.org_id, is_recon_enabled: false, default_profile: None, recon_status: diesel_models::enums::ReconStatus::NotRequested, @@ -342,7 +409,9 @@ enum CreateOrValidateOrganization { #[cfg(any(feature = "v1", feature = "v2"))] Create, /// Validates if this organization exists in the records - Validate { organization_id: String }, + Validate { + organization_id: id_type::OrganizationId, + }, } #[cfg(feature = "olap")] @@ -355,7 +424,7 @@ impl CreateOrValidateOrganization { /// Create an action to either create or validate the given organization_id /// If organization_id is passed, then validate if this organization exists /// If not passed, create a new organization - fn new(organization_id: Option) -> Self { + fn new(organization_id: Option) -> Self { if let Some(organization_id) = organization_id { Self::Validate { organization_id } } else { @@ -365,34 +434,33 @@ impl CreateOrValidateOrganization { #[cfg(all(feature = "v2", feature = "merchant_account_v2", feature = "olap"))] /// Create an action to validate the provided organization_id - fn new(organization_id: String) -> Self { + fn new(organization_id: id_type::OrganizationId) -> Self { Self::Validate { organization_id } } #[cfg(feature = "olap")] /// Apply the action, whether to create the organization or validate the given organization_id - async fn create_or_validate(&self, db: &dyn StorageInterface) -> RouterResult { - Ok(match self { + async fn create_or_validate( + &self, + db: &dyn StorageInterface, + ) -> RouterResult { + match self { #[cfg(any(feature = "v1", feature = "v2"))] Self::Create => { let new_organization = api_models::organization::OrganizationNew::new(None); let db_organization = ForeignFrom::foreign_from(new_organization); - let organization = db - .insert_organization(db_organization) + db.insert_organization(db_organization) .await .to_duplicate_response(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error when creating organization")?; - organization.org_id + .attach_printable("Error when creating organization") } - Self::Validate { organization_id } => { - db.find_organization_by_org_id(organization_id) - .await - .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { - message: "organization with the given id does not exist".to_string(), - })?; - organization_id.to_string() - } - }) + Self::Validate { organization_id } => db + .find_organization_by_org_id(organization_id) + .await + .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { + message: "organization with the given id does not exist".to_string(), + }), + } } } diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index 34210cf6de..7a8f143f0b 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -36,6 +36,7 @@ pub mod user; pub mod user_authentication_method; pub mod user_key_store; pub mod user_role; +use common_utils::id_type; use diesel_models::{ fraud_check::{FraudCheck, FraudCheckUpdate}, organization::{Organization, OrganizationNew, OrganizationUpdate}, @@ -346,14 +347,14 @@ impl OrganizationInterface for KafkaStore { } async fn find_organization_by_org_id( &self, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult { self.diesel_store.find_organization_by_org_id(org_id).await } async fn update_organization_by_org_id( &self, - org_id: &str, + org_id: &id_type::OrganizationId, update: OrganizationUpdate, ) -> CustomResult { self.diesel_store diff --git a/crates/router/src/db/dashboard_metadata.rs b/crates/router/src/db/dashboard_metadata.rs index df36eb85ec..2cbe6ad975 100644 --- a/crates/router/src/db/dashboard_metadata.rs +++ b/crates/router/src/db/dashboard_metadata.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel_models::{enums, user::dashboard_metadata as storage}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -20,7 +21,7 @@ pub trait DashboardMetadataInterface { &self, user_id: Option, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, data_key: enums::DashboardMetadata, dashboard_metadata_update: storage::DashboardMetadataUpdate, ) -> CustomResult; @@ -29,14 +30,14 @@ pub trait DashboardMetadataInterface { &self, user_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, data_keys: Vec, ) -> CustomResult, errors::StorageError>; async fn find_merchant_scoped_dashboard_metadata( &self, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, data_keys: Vec, ) -> CustomResult, errors::StorageError>; @@ -73,7 +74,7 @@ impl DashboardMetadataInterface for Store { &self, user_id: Option, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, data_key: enums::DashboardMetadata, dashboard_metadata_update: storage::DashboardMetadataUpdate, ) -> CustomResult { @@ -95,7 +96,7 @@ impl DashboardMetadataInterface for Store { &self, user_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, data_keys: Vec, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_write(self).await?; @@ -114,7 +115,7 @@ impl DashboardMetadataInterface for Store { async fn find_merchant_scoped_dashboard_metadata( &self, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, data_keys: Vec, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_write(self).await?; @@ -202,7 +203,7 @@ impl DashboardMetadataInterface for MockDb { &self, user_id: Option, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, data_key: enums::DashboardMetadata, dashboard_metadata_update: storage::DashboardMetadataUpdate, ) -> CustomResult { @@ -237,7 +238,7 @@ impl DashboardMetadataInterface for MockDb { &self, user_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, data_keys: Vec, ) -> CustomResult, errors::StorageError> { let dashboard_metadata = self.dashboard_metadata.lock().await; @@ -250,7 +251,7 @@ impl DashboardMetadataInterface for MockDb { .map(|user_id_inner| user_id_inner == user_id) .unwrap_or(false) && metadata_inner.merchant_id == merchant_id - && metadata_inner.org_id == org_id + && metadata_inner.org_id == *org_id && data_keys.contains(&metadata_inner.data_key) }) .cloned() @@ -259,7 +260,7 @@ impl DashboardMetadataInterface for MockDb { if query_result.is_empty() { return Err(errors::StorageError::ValueNotFound(format!( "No dashboard_metadata available for user_id = {user_id},\ - merchant_id = {merchant_id}, org_id = {org_id} and data_keys = {data_keys:?}", + merchant_id = {merchant_id}, org_id = {org_id:?} and data_keys = {data_keys:?}", )) .into()); } @@ -269,7 +270,7 @@ impl DashboardMetadataInterface for MockDb { async fn find_merchant_scoped_dashboard_metadata( &self, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, data_keys: Vec, ) -> CustomResult, errors::StorageError> { let dashboard_metadata = self.dashboard_metadata.lock().await; @@ -277,7 +278,7 @@ impl DashboardMetadataInterface for MockDb { .iter() .filter(|metadata_inner| { metadata_inner.merchant_id == merchant_id - && metadata_inner.org_id == org_id + && metadata_inner.org_id == *org_id && data_keys.contains(&metadata_inner.data_key) }) .cloned() @@ -286,7 +287,7 @@ impl DashboardMetadataInterface for MockDb { if query_result.is_empty() { return Err(errors::StorageError::ValueNotFound(format!( "No dashboard_metadata available for merchant_id = {merchant_id},\ - org_id = {org_id} and data_keyss = {data_keys:?}", + org_id = {org_id:?} and data_keyss = {data_keys:?}", )) .into()); } diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index b9d2f0907a..030af82dc9 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -2563,7 +2563,7 @@ impl UserRoleInterface for KafkaStore { async fn update_user_roles_by_user_id_org_id( &self, user_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, update: user_storage::UserRoleUpdate, ) -> CustomResult, errors::StorageError> { self.diesel_store @@ -2592,7 +2592,7 @@ impl UserRoleInterface for KafkaStore { &self, from_user_id: &str, to_user_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult<(), errors::StorageError> { self.diesel_store .transfer_org_ownership_between_users(from_user_id, to_user_id, org_id) @@ -2622,7 +2622,7 @@ impl DashboardMetadataInterface for KafkaStore { &self, user_id: Option, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, data_key: enums::DashboardMetadata, dashboard_metadata_update: storage::DashboardMetadataUpdate, ) -> CustomResult { @@ -2641,7 +2641,7 @@ impl DashboardMetadataInterface for KafkaStore { &self, user_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, data_keys: Vec, ) -> CustomResult, errors::StorageError> { self.diesel_store @@ -2652,7 +2652,7 @@ impl DashboardMetadataInterface for KafkaStore { async fn find_merchant_scoped_dashboard_metadata( &self, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, data_keys: Vec, ) -> CustomResult, errors::StorageError> { self.diesel_store @@ -2949,7 +2949,7 @@ impl RoleInterface for KafkaStore { &self, role_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult { self.diesel_store .find_role_by_role_id_in_merchant_scope(role_id, merchant_id, org_id) @@ -2976,7 +2976,7 @@ impl RoleInterface for KafkaStore { async fn list_all_roles( &self, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult, errors::StorageError> { self.diesel_store.list_all_roles(merchant_id, org_id).await } diff --git a/crates/router/src/db/organization.rs b/crates/router/src/db/organization.rs index 0fe9ea9a18..e276e1e542 100644 --- a/crates/router/src/db/organization.rs +++ b/crates/router/src/db/organization.rs @@ -1,4 +1,4 @@ -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, id_type}; use diesel_models::organization as storage; use error_stack::report; use router_env::{instrument, tracing}; @@ -14,12 +14,12 @@ pub trait OrganizationInterface { async fn find_organization_by_org_id( &self, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult; async fn update_organization_by_org_id( &self, - user_id: &str, + org_id: &id_type::OrganizationId, update: storage::OrganizationUpdate, ) -> CustomResult; } @@ -41,10 +41,10 @@ impl OrganizationInterface for Store { #[instrument(skip_all)] async fn find_organization_by_org_id( &self, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; - storage::Organization::find_by_org_id(&conn, org_id.to_string()) + storage::Organization::find_by_org_id(&conn, org_id.to_owned()) .await .map_err(|error| report!(errors::StorageError::from(error))) } @@ -52,12 +52,12 @@ impl OrganizationInterface for Store { #[instrument(skip_all)] async fn update_organization_by_org_id( &self, - org_id: &str, + org_id: &id_type::OrganizationId, update: storage::OrganizationUpdate, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; - storage::Organization::update_by_org_id(&conn, org_id.to_string(), update) + storage::Organization::update_by_org_id(&conn, org_id.to_owned(), update) .await .map_err(|error| report!(errors::StorageError::from(error))) } @@ -90,17 +90,18 @@ impl OrganizationInterface for super::MockDb { async fn find_organization_by_org_id( &self, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult { let organizations = self.organizations.lock().await; organizations .iter() - .find(|org| org.org_id == org_id) + .find(|org| org.org_id == *org_id) .cloned() .ok_or( errors::StorageError::ValueNotFound(format!( - "No organization available for org_id = {org_id}" + "No organization available for org_id = {:?}", + org_id )) .into(), ) @@ -108,14 +109,14 @@ impl OrganizationInterface for super::MockDb { async fn update_organization_by_org_id( &self, - org_id: &str, + org_id: &id_type::OrganizationId, update: storage::OrganizationUpdate, ) -> CustomResult { let mut organizations = self.organizations.lock().await; organizations .iter_mut() - .find(|org| org.org_id == org_id) + .find(|org| org.org_id == *org_id) .map(|org| match &update { storage::OrganizationUpdate::Update { org_name } => storage::Organization { org_name: org_name.clone(), @@ -124,7 +125,8 @@ impl OrganizationInterface for super::MockDb { }) .ok_or( errors::StorageError::ValueNotFound(format!( - "No organization available for org_id = {org_id}" + "No organization available for org_id = {:?}", + org_id )) .into(), ) diff --git a/crates/router/src/db/role.rs b/crates/router/src/db/role.rs index 54b7f6e38d..8891533338 100644 --- a/crates/router/src/db/role.rs +++ b/crates/router/src/db/role.rs @@ -1,4 +1,5 @@ use common_enums::enums; +use common_utils::id_type; use diesel_models::role as storage; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -26,7 +27,7 @@ pub trait RoleInterface { &self, role_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult; async fn update_role_by_role_id( @@ -43,7 +44,7 @@ pub trait RoleInterface { async fn list_all_roles( &self, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult, errors::StorageError>; } @@ -76,7 +77,7 @@ impl RoleInterface for Store { &self, role_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; storage::Role::find_by_role_id_in_merchant_scope(&conn, role_id, merchant_id, org_id) @@ -111,7 +112,7 @@ impl RoleInterface for Store { async fn list_all_roles( &self, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_write(self).await?; storage::Role::list_roles(&conn, merchant_id, org_id) @@ -174,7 +175,7 @@ impl RoleInterface for MockDb { &self, role_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult { let roles = self.roles.lock().await; roles @@ -182,13 +183,13 @@ impl RoleInterface for MockDb { .find(|role| { role.role_id == role_id && (role.merchant_id == merchant_id - || (role.org_id == org_id && role.scope == enums::RoleScope::Organization)) + || (role.org_id == *org_id && role.scope == enums::RoleScope::Organization)) }) .cloned() .ok_or( errors::StorageError::ValueNotFound(format!( "No role available in merchant scope for role_id = {role_id}, \ - merchant_id = {merchant_id} and org_id = {org_id}" + merchant_id = {merchant_id} and org_id = {org_id:?}" )) .into(), ) @@ -246,7 +247,7 @@ impl RoleInterface for MockDb { async fn list_all_roles( &self, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult, errors::StorageError> { let roles = self.roles.lock().await; @@ -254,7 +255,7 @@ impl RoleInterface for MockDb { .iter() .filter(|role| { role.merchant_id == merchant_id - || (role.org_id == org_id + || (role.org_id == *org_id && role.scope == diesel_models::enums::RoleScope::Organization) }) .cloned() @@ -262,7 +263,7 @@ impl RoleInterface for MockDb { if roles_list.is_empty() { return Err(errors::StorageError::ValueNotFound(format!( - "No role found for merchant id = {} and org_id = {}", + "No role found for merchant id = {} and org_id = {:?}", merchant_id, org_id )) .into()); diff --git a/crates/router/src/db/user_role.rs b/crates/router/src/db/user_role.rs index 5d05b55603..50ea4ecc9a 100644 --- a/crates/router/src/db/user_role.rs +++ b/crates/router/src/db/user_role.rs @@ -1,6 +1,7 @@ use std::{collections::HashSet, ops::Not}; use async_bb8_diesel::AsyncConnection; +use common_utils::id_type; use diesel_models::{enums, user_role as storage}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -40,7 +41,7 @@ pub trait UserRoleInterface { async fn update_user_roles_by_user_id_org_id( &self, user_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, update: storage::UserRoleUpdate, ) -> CustomResult, errors::StorageError>; @@ -64,7 +65,7 @@ pub trait UserRoleInterface { &self, from_user_id: &str, to_user_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult<(), errors::StorageError>; } @@ -131,7 +132,7 @@ impl UserRoleInterface for Store { async fn update_user_roles_by_user_id_org_id( &self, user_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, update: storage::UserRoleUpdate, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_write(self).await?; @@ -189,7 +190,7 @@ impl UserRoleInterface for Store { &self, from_user_id: &str, to_user_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult<(), errors::StorageError> { let conn = connection::pg_connection_write(self) .await @@ -237,7 +238,7 @@ impl UserRoleInterface for Store { user_id: to_user_id.to_string(), merchant_id: old_role.merchant_id, role_id: consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), - org_id: org_id.to_string(), + org_id: org_id.to_owned(), status: enums::UserStatus::Active, created_by: from_user_id.to_string(), last_modified_by: from_user_id.to_string(), @@ -374,13 +375,13 @@ impl UserRoleInterface for MockDb { async fn update_user_roles_by_user_id_org_id( &self, user_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, update: storage::UserRoleUpdate, ) -> CustomResult, errors::StorageError> { let mut user_roles = self.user_roles.lock().await; let mut updated_user_roles = Vec::new(); for user_role in user_roles.iter_mut() { - if user_role.user_id == user_id && user_role.org_id == org_id { + if user_role.user_id == user_id && user_role.org_id == *org_id { match &update { storage::UserRoleUpdate::UpdateRole { role_id, @@ -402,7 +403,7 @@ impl UserRoleInterface for MockDb { } if updated_user_roles.is_empty() { Err(errors::StorageError::ValueNotFound(format!( - "No user role available for user_id = {user_id} and org_id = {org_id}" + "No user role available for user_id = {user_id} and org_id = {org_id:?}" )) .into()) } else { @@ -414,7 +415,7 @@ impl UserRoleInterface for MockDb { &self, from_user_id: &str, to_user_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult<(), errors::StorageError> { let old_org_admin_user_roles = self .update_user_roles_by_user_id_org_id( @@ -453,7 +454,7 @@ impl UserRoleInterface for MockDb { user_id: to_user_id.to_string(), merchant_id: old_roles.merchant_id, role_id: consts::user_role::ROLE_ID_ORGANIZATION_ADMIN.to_string(), - org_id: org_id.to_string(), + org_id: org_id.to_owned(), status: enums::UserStatus::Active, created_by: from_user_id.to_string(), last_modified_by: from_user_id.to_string(), diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 530f3f98ce..6581436c25 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -146,6 +146,7 @@ pub fn mk_app( #[cfg(feature = "olap")] { server_app = server_app + .service(routes::Organization::server(state.clone())) .service(routes::MerchantAccount::server(state.clone())) .service(routes::ApiKeys::server(state.clone())) .service(routes::Files::server(state.clone())) diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index df7b64e6e8..304a0cde0b 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -64,7 +64,7 @@ pub use self::app::{ Refunds, SessionState, User, Webhooks, }; #[cfg(feature = "olap")] -pub use self::app::{Blocklist, Routing, Verify, WebhookEvents}; +pub use self::app::{Blocklist, Organization, Routing, Verify, WebhookEvents}; #[cfg(feature = "payouts")] pub use self::app::{PayoutLink, Payouts}; #[cfg(all( diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index fe791bfaea..e6c4ff8dc6 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -1,4 +1,6 @@ use actix_web::{web, HttpRequest, HttpResponse}; +#[cfg(feature = "olap")] +use common_utils::id_type; use router_env::{instrument, tracing, Flow}; use super::app::AppState; @@ -8,6 +10,71 @@ use crate::{ types::api::admin, }; +#[cfg(feature = "olap")] +#[instrument(skip_all, fields(flow = ?Flow::OrganizationCreate))] +pub async fn organization_create( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::OrganizationCreate; + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, _, req, _| create_organization(state, req), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(feature = "olap")] +#[instrument(skip_all, fields(flow = ?Flow::OrganizationUpdate))] +pub async fn organization_update( + state: web::Data, + req: HttpRequest, + org_id: web::Path, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::OrganizationUpdate; + let organization_id = org_id.into_inner(); + let org_id = admin::OrganizationId { organization_id }; + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, _, req, _| update_organization(state, org_id.clone(), req), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(feature = "olap")] +#[instrument(skip_all, fields(flow = ?Flow::OrganizationRetrieve))] +pub async fn organization_retrieve( + state: web::Data, + req: HttpRequest, + org_id: web::Path, +) -> HttpResponse { + let flow = Flow::OrganizationRetrieve; + let organization_id = org_id.into_inner(); + let payload = admin::OrganizationId { organization_id }; + Box::pin(api::server_wrap( + flow, + state, + &req, + payload, + |state, _, req, _| get_organization(state, req), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + #[cfg(feature = "olap")] #[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))] pub async fn merchant_account_create( @@ -51,11 +118,9 @@ pub async fn retrieve_merchant_account( ) -> HttpResponse { let flow = Flow::MerchantsAccountRetrieve; let merchant_id = mid.into_inner(); - let payload = web::Json(admin::MerchantId { - merchant_id: merchant_id.to_owned(), - }) - .into_inner(); - + let payload = admin::MerchantId { + merchant_id: merchant_id.clone(), + }; api::server_wrap( flow, state, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 4f79670cac..0970da6e0a 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1028,6 +1028,22 @@ impl Blocklist { } } +#[cfg(feature = "olap")] +pub struct Organization; +#[cfg(feature = "olap")] +impl Organization { + pub fn server(state: AppState) -> Scope { + web::scope("/organization") + .app_data(web::Data::new(state)) + .service(web::resource("").route(web::post().to(organization_create))) + .service( + web::resource("/{id}") + .route(web::get().to(organization_retrieve)) + .route(web::put().to(organization_update)), + ) + } +} + pub struct MerchantAccount; #[cfg(all(feature = "v2", feature = "olap", feature = "merchant_account_v2"))] diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 8f0c1aa804..9618971e52 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -6,6 +6,7 @@ pub enum ApiIdentifier { Payments, Refunds, Webhooks, + Organization, MerchantAccount, MerchantConnector, Configs, @@ -48,6 +49,10 @@ impl From for ApiIdentifier { | Flow::MerchantTransferKey | Flow::MerchantAccountList => Self::MerchantAccount, + Flow::OrganizationCreate | Flow::OrganizationRetrieve | Flow::OrganizationUpdate => { + Self::Organization + } + Flow::RoutingCreateConfig | Flow::RoutingLinkConfig | Flow::RoutingUnlinkConfig diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 22c7f25ff8..001eb3d168 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -7,7 +7,7 @@ use api_models::{ }; use async_trait::async_trait; use common_enums::TokenPurpose; -use common_utils::date_time; +use common_utils::{date_time, id_type}; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; @@ -169,7 +169,7 @@ pub struct AuthToken { pub merchant_id: String, pub role_id: String, pub exp: u64, - pub org_id: String, + pub org_id: id_type::OrganizationId, } #[cfg(feature = "olap")] @@ -179,7 +179,7 @@ impl AuthToken { merchant_id: String, role_id: String, settings: &Settings, - org_id: String, + org_id: id_type::OrganizationId, ) -> UserResult { let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS); let exp = jwt::generate_exp(exp_duration)?.as_secs(); @@ -199,7 +199,7 @@ pub struct UserFromToken { pub user_id: String, pub merchant_id: String, pub role_id: String, - pub org_id: String, + pub org_id: id_type::OrganizationId, } pub struct UserIdFromAuth { diff --git a/crates/router/src/services/authorization.rs b/crates/router/src/services/authorization.rs index 530f538cfd..2fb809d482 100644 --- a/crates/router/src/services/authorization.rs +++ b/crates/router/src/services/authorization.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use common_enums::PermissionGroup; +use common_utils::id_type; use error_stack::ResultExt; use redis_interface::RedisConnectionPool; use router_env::logger; @@ -79,7 +80,7 @@ async fn get_permissions_from_db( state: &A, role_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> RouterResult> where A: SessionStateInfo + Sync, diff --git a/crates/router/src/services/authorization/roles.rs b/crates/router/src/services/authorization/roles.rs index 8e5cb6fbea..5ffd99a2d0 100644 --- a/crates/router/src/services/authorization/roles.rs +++ b/crates/router/src/services/authorization/roles.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use common_enums::{PermissionGroup, RoleScope}; -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, id_type}; use super::{permission_groups::get_permissions_vec, permissions::Permission}; use crate::{core::errors, routes::SessionState}; @@ -70,7 +70,7 @@ impl RoleInfo { state: &SessionState, role_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> CustomResult { if let Some(role) = predefined_roles::PREDEFINED_ROLES.get(role_id) { Ok(role.clone()) diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index f3eecc8c76..2228293320 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -1,12 +1,15 @@ use std::collections::HashMap; -pub use api_models::admin::{ - BusinessProfileCreate, BusinessProfileResponse, BusinessProfileUpdate, MerchantAccountCreate, - MerchantAccountDeleteResponse, MerchantAccountResponse, MerchantAccountUpdate, - MerchantConnectorCreate, MerchantConnectorDeleteResponse, MerchantConnectorDetails, - MerchantConnectorDetailsWrap, MerchantConnectorId, MerchantConnectorResponse, MerchantDetails, - MerchantId, PaymentMethodsEnabled, ToggleAllKVRequest, ToggleAllKVResponse, ToggleKVRequest, - ToggleKVResponse, WebhookDetails, +pub use api_models::{ + admin::{ + BusinessProfileCreate, BusinessProfileResponse, BusinessProfileUpdate, + MerchantAccountCreate, MerchantAccountDeleteResponse, MerchantAccountResponse, + MerchantAccountUpdate, MerchantConnectorCreate, MerchantConnectorDeleteResponse, + MerchantConnectorDetails, MerchantConnectorDetailsWrap, MerchantConnectorId, + MerchantConnectorResponse, MerchantDetails, MerchantId, PaymentMethodsEnabled, + ToggleAllKVRequest, ToggleAllKVResponse, ToggleKVRequest, ToggleKVResponse, WebhookDetails, + }, + organization::{OrganizationId, OrganizationRequest, OrganizationResponse}, }; use common_utils::{ ext_traits::{AsyncExt, Encode, ValueExt}, @@ -21,9 +24,18 @@ use masking::{ExposeInterface, PeekInterface, Secret}; use crate::{ core::{errors, payment_methods::cards::create_encrypted_data}, routes::SessionState, - types::{domain, storage, transformers::ForeignTryFrom}, + types::{domain, storage, transformers::ForeignTryFrom, ForeignFrom}, }; +impl ForeignFrom for OrganizationResponse { + fn foreign_from(org: diesel_models::organization::Organization) -> Self { + Self { + organization_id: org.org_id, + organization_name: org.org_name, + } + } +} + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "merchant_account_v2") diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index d66f09f442..e7813ec3d2 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -4,10 +4,8 @@ use api_models::{ admin as admin_api, organization as api_org, user as user_api, user_role as user_role_api, }; use common_enums::TokenPurpose; -#[cfg(any(feature = "v1", feature = "v2"))] -use common_utils::id_type; use common_utils::{ - crypto::Encryptable, errors::CustomResult, new_type::MerchantName, pii, + crypto::Encryptable, errors::CustomResult, id_type, new_type::MerchantName, pii, types::keymanager::Identifier, }; use diesel_models::{ @@ -256,7 +254,7 @@ impl NewUserOrganization { .attach_printable("Error while inserting organization") } - pub fn get_organization_id(&self) -> String { + pub fn get_organization_id(&self) -> id_type::OrganizationId { self.0.org_id.clone() } } @@ -288,8 +286,10 @@ impl From for NewUserOrganization { } } -impl From<(user_api::CreateInternalUserRequest, String)> for NewUserOrganization { - fn from((_value, org_id): (user_api::CreateInternalUserRequest, String)) -> Self { +impl From<(user_api::CreateInternalUserRequest, id_type::OrganizationId)> for NewUserOrganization { + fn from( + (_value, org_id): (user_api::CreateInternalUserRequest, id_type::OrganizationId), + ) -> Self { let new_organization = api_org::OrganizationNew { org_id, org_name: None, @@ -511,10 +511,12 @@ impl TryFrom for NewUserMerchant { } } -impl TryFrom<(user_api::CreateInternalUserRequest, String)> for NewUserMerchant { +impl TryFrom<(user_api::CreateInternalUserRequest, id_type::OrganizationId)> for NewUserMerchant { type Error = error_stack::Report; - fn try_from(value: (user_api::CreateInternalUserRequest, String)) -> UserResult { + fn try_from( + value: (user_api::CreateInternalUserRequest, id_type::OrganizationId), + ) -> UserResult { let merchant_id = MerchantId::new(consts::user_role::INTERNAL_USER_MERCHANT_ID.to_string())?; let new_organization = NewUserOrganization::from(value); @@ -756,11 +758,11 @@ impl TryFrom for NewUser { } } -impl TryFrom<(user_api::CreateInternalUserRequest, String)> for NewUser { +impl TryFrom<(user_api::CreateInternalUserRequest, id_type::OrganizationId)> for NewUser { type Error = error_stack::Report; fn try_from( - (value, org_id): (user_api::CreateInternalUserRequest, String), + (value, org_id): (user_api::CreateInternalUserRequest, id_type::OrganizationId), ) -> UserResult { let user_id = uuid::Uuid::new_v4().to_string(); let email = value.email.clone().try_into()?; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 4805356d3e..b4ded74d3a 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1285,6 +1285,18 @@ impl ForeignFrom } } +impl ForeignFrom + for diesel_models::organization::OrganizationNew +{ + fn foreign_from(item: api_models::organization::OrganizationRequest) -> Self { + let org_new = api_models::organization::OrganizationNew::new(None); + Self { + org_id: org_new.org_id, + org_name: item.organization_name, + } + } +} + impl ForeignFrom for storage::GatewayStatusMappingNew { fn foreign_from(value: gsm_api_types::GsmCreateRequest) -> Self { Self { diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index ccb9ed7d37..6c52ecda4c 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -2,7 +2,9 @@ use std::{collections::HashMap, sync::Arc}; use api_models::user as user_api; use common_enums::UserAuthType; -use common_utils::{encryption::Encryption, errors::CustomResult, types::keymanager::Identifier}; +use common_utils::{ + encryption::Encryption, errors::CustomResult, id_type, types::keymanager::Identifier, +}; use diesel_models::{enums::UserStatus, user_role::UserRole}; use error_stack::ResultExt; use masking::{ExposeInterface, Secret}; @@ -99,7 +101,7 @@ pub async fn generate_jwt_auth_token_with_custom_role_attributes( state: &SessionState, user: &UserFromStorage, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, role_id: String, ) -> UserResult> { let token = AuthToken::new_token( diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index 5622a43f4d..225dc81765 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -4,6 +4,7 @@ use actix_web::http::header::HeaderMap; use api_models::user::dashboard_metadata::{ GetMetaDataRequest, GetMultipleMetaDataPayload, ProdIntent, SetMetaDataRequest, }; +use common_utils::id_type; use diesel_models::{ enums::DashboardMetadata as DBEnum, user::dashboard_metadata::{DashboardMetadata, DashboardMetadataNew, DashboardMetadataUpdate}, @@ -20,7 +21,7 @@ pub async fn insert_merchant_scoped_metadata_to_db( state: &SessionState, user_id: String, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, metadata_key: DBEnum, metadata_value: impl serde::Serialize, ) -> UserResult { @@ -53,7 +54,7 @@ pub async fn insert_user_scoped_metadata_to_db( state: &SessionState, user_id: String, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, metadata_key: DBEnum, metadata_value: impl serde::Serialize, ) -> UserResult { @@ -86,7 +87,7 @@ pub async fn insert_user_scoped_metadata_to_db( pub async fn get_merchant_scoped_metadata_from_db( state: &SessionState, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, metadata_keys: Vec, ) -> UserResult> { state @@ -100,7 +101,7 @@ pub async fn get_user_scoped_metadata_from_db( state: &SessionState, user_id: String, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, metadata_keys: Vec, ) -> UserResult> { match state @@ -124,7 +125,7 @@ pub async fn update_merchant_scoped_metadata( state: &SessionState, user_id: String, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, metadata_key: DBEnum, metadata_value: impl serde::Serialize, ) -> UserResult { @@ -152,7 +153,7 @@ pub async fn update_user_scoped_metadata( state: &SessionState, user_id: String, merchant_id: String, - org_id: String, + org_id: id_type::OrganizationId, metadata_key: DBEnum, metadata_value: impl serde::Serialize, ) -> UserResult { diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs index b1fa4c3a17..d304c31c1c 100644 --- a/crates/router/src/utils/user_role.rs +++ b/crates/router/src/utils/user_role.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use api_models::user_role as user_role_api; use common_enums::PermissionGroup; +use common_utils::id_type; use diesel_models::user_role::UserRole; use error_stack::{report, ResultExt}; use router_env::logger; @@ -77,7 +78,7 @@ pub async fn validate_role_name( state: &SessionState, role_name: &domain::RoleName, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> UserResult<()> { let role_name_str = role_name.clone().get_role_name(); @@ -109,7 +110,7 @@ pub async fn set_role_permissions_in_cache_by_user_role( state, user_role.role_id.as_str(), user_role.merchant_id.as_str(), - user_role.org_id.as_str(), + &user_role.org_id, ) .await .map_err(|e| logger::error!("Error setting permissions in cache {:?}", e)) @@ -120,7 +121,7 @@ pub async fn set_role_permissions_in_cache_if_required( state: &SessionState, role_id: &str, merchant_id: &str, - org_id: &str, + org_id: &id_type::OrganizationId, ) -> UserResult<()> { if roles::predefined_roles::PREDEFINED_ROLES.contains_key(role_id) { return Ok(()); diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 8ff019f9b5..cefc2fbd1d 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -61,6 +61,12 @@ pub enum Flow { HealthCheck, /// Deep health Check DeepHealthCheck, + /// Organization create flow + OrganizationCreate, + /// Organization retrieve flow + OrganizationRetrieve, + /// Organization update flow + OrganizationUpdate, /// Merchants account create flow. MerchantsAccountCreate, /// Merchants account retrieve flow.