Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(db): implement MerchantAccountInteraface for Mockdb #6283

Merged
merged 13 commits into from
Nov 5, 2024
68 changes: 68 additions & 0 deletions crates/diesel_models/src/merchant_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,71 @@ pub struct MerchantAccountUpdateInternal {
pub payment_link_config: Option<serde_json::Value>,
pub pm_collect_link_config: Option<serde_json::Value>,
}

#[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: 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,
}
}
}
2 changes: 1 addition & 1 deletion crates/hyperswitch_domain_models/src/merchant_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
222 changes: 178 additions & 44 deletions crates/router/src/db/merchant_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,91 +482,225 @@ impl MerchantAccountInterface for MockDb {
merchant_key_store: &domain::MerchantKeyStore,
) -> CustomResult<domain::MerchantAccount, errors::StorageError> {
let accounts = self.merchant_accounts.lock().await;
let account: Option<domain::MerchantAccount> = 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<domain::MerchantAccount, errors::StorageError> {
// [#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<domain::MerchantAccount, errors::StorageError> {
// [#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<authentication::AuthenticationData, errors::StorageError> {
// [#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<usize, errors::StorageError> {
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<bool, errors::StorageError> {
// [#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<Vec<domain::MerchantAccount>, 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<common_utils::id_type::MerchantId>,
state: &KeyManagerState,
merchant_ids: Vec<common_utils::id_type::MerchantId>,
) -> CustomResult<Vec<domain::MerchantAccount>, 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()
}
}

Expand Down
1 change: 1 addition & 0 deletions docker-compose-development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading