diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index b1dcc1d3476..12d51311e87 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -260,6 +260,36 @@ pub struct MerchantAccountUpdateInternal { pub recon_status: Option, } +#[cfg(feature = "v2")] +impl MerchantAccountUpdateInternal { + pub fn apply_changeset(self, source: MerchantAccount) -> MerchantAccount { + let Self { + merchant_name, + merchant_details, + publishable_key, + storage_scheme, + metadata, + modified_at, + organization_id, + recon_status, + } = self; + + MerchantAccount { + merchant_name: merchant_name.or(source.merchant_name), + merchant_details: merchant_details.or(source.merchant_details), + publishable_key: publishable_key.or(source.publishable_key), + storage_scheme: storage_scheme.unwrap_or(source.storage_scheme), + metadata: metadata.or(source.metadata), + created_at: source.created_at, + modified_at, + organization_id: organization_id.unwrap_or(source.organization_id), + recon_status: recon_status.unwrap_or(source.recon_status), + version: source.version, + id: source.id, + } + } +} + #[cfg(feature = "v1")] #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] #[diesel(table_name = merchant_account)] @@ -290,3 +320,71 @@ pub struct MerchantAccountUpdateInternal { pub payment_link_config: Option, pub pm_collect_link_config: Option, } + +#[cfg(feature = "v1")] +impl MerchantAccountUpdateInternal { + pub fn apply_changeset(self, source: MerchantAccount) -> MerchantAccount { + let Self { + merchant_name, + merchant_details, + return_url, + webhook_details, + sub_merchants_enabled, + parent_merchant_id, + enable_payment_response_hash, + payment_response_hash_key, + redirect_to_merchant_with_http_post, + publishable_key, + storage_scheme, + locker_id, + metadata, + routing_algorithm, + primary_business_details, + modified_at, + intent_fulfillment_time, + frm_routing_algorithm, + payout_routing_algorithm, + organization_id, + is_recon_enabled, + default_profile, + recon_status, + payment_link_config, + pm_collect_link_config, + } = self; + + MerchantAccount { + merchant_id: source.merchant_id, + return_url: return_url.or(source.return_url), + enable_payment_response_hash: enable_payment_response_hash + .unwrap_or(source.enable_payment_response_hash), + payment_response_hash_key: payment_response_hash_key + .or(source.payment_response_hash_key), + redirect_to_merchant_with_http_post: redirect_to_merchant_with_http_post + .unwrap_or(source.redirect_to_merchant_with_http_post), + merchant_name: merchant_name.or(source.merchant_name), + merchant_details: merchant_details.or(source.merchant_details), + webhook_details: webhook_details.or(source.webhook_details), + sub_merchants_enabled: sub_merchants_enabled.or(source.sub_merchants_enabled), + parent_merchant_id: parent_merchant_id.or(source.parent_merchant_id), + publishable_key: publishable_key.or(source.publishable_key), + storage_scheme: storage_scheme.unwrap_or(source.storage_scheme), + locker_id: locker_id.or(source.locker_id), + metadata: metadata.or(source.metadata), + routing_algorithm: routing_algorithm.or(source.routing_algorithm), + primary_business_details: primary_business_details + .unwrap_or(source.primary_business_details), + intent_fulfillment_time: intent_fulfillment_time.or(source.intent_fulfillment_time), + created_at: source.created_at, + modified_at, + frm_routing_algorithm: frm_routing_algorithm.or(source.frm_routing_algorithm), + payout_routing_algorithm: payout_routing_algorithm.or(source.payout_routing_algorithm), + organization_id: organization_id.unwrap_or(source.organization_id), + is_recon_enabled: is_recon_enabled.unwrap_or(source.is_recon_enabled), + default_profile: default_profile.unwrap_or(source.default_profile), + recon_status: recon_status.unwrap_or(source.recon_status), + payment_link_config: payment_link_config.or(source.payment_link_config), + pm_collect_link_config: pm_collect_link_config.or(source.pm_collect_link_config), + version: source.version, + } + } +} diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index 3e860d36284..1e3302dab4a 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -201,7 +201,7 @@ impl MerchantAccount { #[cfg(feature = "v1")] #[allow(clippy::large_enum_variant)] -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum MerchantAccountUpdate { Update { merchant_name: OptionalEncryptableName, @@ -237,7 +237,7 @@ pub enum MerchantAccountUpdate { #[cfg(feature = "v2")] #[allow(clippy::large_enum_variant)] -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum MerchantAccountUpdate { Update { merchant_name: OptionalEncryptableName, diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs index 13a778001ae..39844503892 100644 --- a/crates/router/src/db/merchant_account.rs +++ b/crates/router/src/db/merchant_account.rs @@ -482,91 +482,225 @@ impl MerchantAccountInterface for MockDb { merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { let accounts = self.merchant_accounts.lock().await; - let account: Option = accounts + accounts .iter() .find(|account| account.get_id() == merchant_id) .cloned() - .async_map(|a| async { - a.convert( - state, - merchant_key_store.key.get_inner(), - merchant_key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError) - }) + .ok_or(errors::StorageError::ValueNotFound(format!( + "Merchant ID: {:?} not found", + merchant_id + )))? + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone().into(), + ) .await - .transpose()?; - - match account { - Some(account) => Ok(account), - // [#172]: Implement function for `MockDb` - None => Err(errors::StorageError::MockDbError)?, - } + .change_context(errors::StorageError::DecryptionError) } async fn update_merchant( &self, - _state: &KeyManagerState, - _this: domain::MerchantAccount, - _merchant_account: storage::MerchantAccountUpdate, - _merchant_key_store: &domain::MerchantKeyStore, + state: &KeyManagerState, + merchant_account: domain::MerchantAccount, + merchant_account_update: storage::MerchantAccountUpdate, + merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { - // [#172]: Implement function for `MockDb` - Err(errors::StorageError::MockDbError)? + let merchant_id = merchant_account.get_id().to_owned(); + let mut accounts = self.merchant_accounts.lock().await; + accounts + .iter_mut() + .find(|account| account.get_id() == merchant_account.get_id()) + .async_map(|account| async { + let update = MerchantAccountUpdateInternal::from(merchant_account_update) + .apply_changeset( + Conversion::convert(merchant_account) + .await + .change_context(errors::StorageError::EncryptionError)?, + ); + *account = update.clone(); + update + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose()? + .ok_or( + errors::StorageError::ValueNotFound(format!( + "Merchant ID: {:?} not found", + merchant_id + )) + .into(), + ) } async fn update_specific_fields_in_merchant( &self, - _state: &KeyManagerState, - _merchant_id: &common_utils::id_type::MerchantId, - _merchant_account: storage::MerchantAccountUpdate, - _merchant_key_store: &domain::MerchantKeyStore, + state: &KeyManagerState, + merchant_id: &common_utils::id_type::MerchantId, + merchant_account_update: storage::MerchantAccountUpdate, + merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { - // [#TODO]: Implement function for `MockDb` - Err(errors::StorageError::MockDbError)? + let mut accounts = self.merchant_accounts.lock().await; + accounts + .iter_mut() + .find(|account| account.get_id() == merchant_id) + .async_map(|account| async { + let update = MerchantAccountUpdateInternal::from(merchant_account_update) + .apply_changeset(account.clone()); + *account = update.clone(); + update + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose()? + .ok_or( + errors::StorageError::ValueNotFound(format!( + "Merchant ID: {:?} not found", + merchant_id + )) + .into(), + ) } async fn find_merchant_account_by_publishable_key( &self, - _state: &KeyManagerState, - _publishable_key: &str, + state: &KeyManagerState, + publishable_key: &str, ) -> CustomResult { - // [#172]: Implement function for `MockDb` - Err(errors::StorageError::MockDbError)? + let accounts = self.merchant_accounts.lock().await; + let account = accounts + .iter() + .find(|account| { + account + .publishable_key + .as_ref() + .is_some_and(|key| key == publishable_key) + }) + .ok_or(errors::StorageError::ValueNotFound(format!( + "Publishable Key: {} not found", + publishable_key + )))?; + let key_store = self + .get_merchant_key_store_by_merchant_id( + state, + account.get_id(), + &self.get_master_key().to_vec().into(), + ) + .await?; + Ok(authentication::AuthenticationData { + merchant_account: account + .clone() + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?, + + key_store, + profile_id: None, + }) } async fn update_all_merchant_account( &self, - _merchant_account_update: storage::MerchantAccountUpdate, + merchant_account_update: storage::MerchantAccountUpdate, ) -> CustomResult { - Err(errors::StorageError::MockDbError)? + let mut accounts = self.merchant_accounts.lock().await; + Ok(accounts.iter_mut().fold(0, |acc, account| { + let update = MerchantAccountUpdateInternal::from(merchant_account_update.clone()) + .apply_changeset(account.clone()); + *account = update; + acc + 1 + })) } async fn delete_merchant_account_by_merchant_id( &self, - _merchant_id: &common_utils::id_type::MerchantId, + merchant_id: &common_utils::id_type::MerchantId, ) -> CustomResult { - // [#172]: Implement function for `MockDb` - Err(errors::StorageError::MockDbError)? + let mut accounts = self.merchant_accounts.lock().await; + accounts.retain(|x| x.get_id() != merchant_id); + Ok(true) } #[cfg(feature = "olap")] async fn list_merchant_accounts_by_organization_id( &self, - _state: &KeyManagerState, - _organization_id: &common_utils::id_type::OrganizationId, + state: &KeyManagerState, + organization_id: &common_utils::id_type::OrganizationId, ) -> CustomResult, errors::StorageError> { - Err(errors::StorageError::MockDbError)? + let accounts = self.merchant_accounts.lock().await; + let futures = accounts + .iter() + .filter(|account| account.organization_id == *organization_id) + .map(|account| async { + let key_store = self + .get_merchant_key_store_by_merchant_id( + state, + account.get_id(), + &self.get_master_key().to_vec().into(), + ) + .await; + match key_store { + Ok(key) => account + .clone() + .convert(state, key.key.get_inner(), key.merchant_id.clone().into()) + .await + .change_context(errors::StorageError::DecryptionError), + Err(err) => Err(err), + } + }); + futures::future::join_all(futures) + .await + .into_iter() + .collect() } #[cfg(feature = "olap")] async fn list_multiple_merchant_accounts( &self, - _state: &KeyManagerState, - _merchant_ids: Vec, + state: &KeyManagerState, + merchant_ids: Vec, ) -> CustomResult, errors::StorageError> { - Err(errors::StorageError::MockDbError)? + let accounts = self.merchant_accounts.lock().await; + let futures = accounts + .iter() + .filter(|account| merchant_ids.contains(account.get_id())) + .map(|account| async { + let key_store = self + .get_merchant_key_store_by_merchant_id( + state, + account.get_id(), + &self.get_master_key().to_vec().into(), + ) + .await; + match key_store { + Ok(key) => account + .clone() + .convert(state, key.key.get_inner(), key.merchant_id.clone().into()) + .await + .change_context(errors::StorageError::DecryptionError), + Err(err) => Err(err), + } + }); + futures::future::join_all(futures) + .await + .into_iter() + .collect() } } diff --git a/docker-compose-development.yml b/docker-compose-development.yml index 3878f880b06..07a2131c6d4 100644 --- a/docker-compose-development.yml +++ b/docker-compose-development.yml @@ -64,6 +64,7 @@ services: FROM rust:latest RUN apt-get update && \ apt-get install -y protobuf-compiler + RUN rustup component add rustfmt clippy command: cargo run --bin router -- -f ./config/docker_compose.toml working_dir: /app ports: