mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
refactor(settings): make the function to deserialize hashsets more generic (#3104)
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
@ -20,7 +19,7 @@ use redis_interface::RedisSettings;
|
||||
pub use router_env::config::{Log, LogConsole, LogFile, LogTelemetry};
|
||||
use rust_decimal::Decimal;
|
||||
use scheduler::SchedulerSettings;
|
||||
use serde::{de::Error, Deserialize, Deserializer};
|
||||
use serde::Deserialize;
|
||||
use storage_impl::config::QueueStrategy;
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
@ -191,7 +190,7 @@ pub struct ApplepayMerchantConfigs {
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct MultipleApiVersionSupportedConnectors {
|
||||
#[serde(deserialize_with = "connector_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub supported_connectors: HashSet<api_models::enums::Connector>,
|
||||
}
|
||||
|
||||
@ -205,42 +204,13 @@ pub struct TempLockerEnableConfig(pub HashMap<String, TempLockerEnablePaymentMet
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct ConnectorCustomer {
|
||||
#[serde(deserialize_with = "connector_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub connector_list: HashSet<api_models::enums::Connector>,
|
||||
#[cfg(feature = "payouts")]
|
||||
#[serde(deserialize_with = "payout_connector_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub payout_connector_list: HashSet<api_models::enums::PayoutConnectors>,
|
||||
}
|
||||
|
||||
fn connector_deser<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<HashSet<api_models::enums::Connector>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <String>::deserialize(deserializer)?;
|
||||
Ok(value
|
||||
.trim()
|
||||
.split(',')
|
||||
.flat_map(api_models::enums::Connector::from_str)
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
fn payout_connector_deser<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<HashSet<api_models::enums::PayoutConnectors>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <String>::deserialize(deserializer)?;
|
||||
Ok(value
|
||||
.trim()
|
||||
.split(',')
|
||||
.flat_map(api_models::enums::PayoutConnectors::from_str)
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct DummyConnector {
|
||||
@ -281,13 +251,13 @@ pub struct SupportedPaymentMethodTypesForMandate(
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct SupportedConnectorsForMandate {
|
||||
#[serde(deserialize_with = "connector_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub connector_list: HashSet<api_models::enums::Connector>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct PaymentMethodTokenFilter {
|
||||
#[serde(deserialize_with = "pm_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub payment_method: HashSet<diesel_models::enums::PaymentMethod>,
|
||||
pub payment_method_type: Option<PaymentMethodTypeTokenFilter>,
|
||||
pub long_lived_token: bool,
|
||||
@ -304,7 +274,7 @@ pub enum ApplePayPreDecryptFlow {
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct TempLockerEnablePaymentMethodFilter {
|
||||
#[serde(deserialize_with = "pm_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub payment_method: HashSet<diesel_models::enums::PaymentMethod>,
|
||||
}
|
||||
|
||||
@ -316,44 +286,14 @@ pub struct TempLockerEnablePaymentMethodFilter {
|
||||
rename_all = "snake_case"
|
||||
)]
|
||||
pub enum PaymentMethodTypeTokenFilter {
|
||||
#[serde(deserialize_with = "pm_type_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
EnableOnly(HashSet<diesel_models::enums::PaymentMethodType>),
|
||||
#[serde(deserialize_with = "pm_type_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
DisableOnly(HashSet<diesel_models::enums::PaymentMethodType>),
|
||||
#[default]
|
||||
AllAccepted,
|
||||
}
|
||||
|
||||
fn pm_deser<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<HashSet<diesel_models::enums::PaymentMethod>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <String>::deserialize(deserializer)?;
|
||||
value
|
||||
.trim()
|
||||
.split(',')
|
||||
.map(diesel_models::enums::PaymentMethod::from_str)
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
|
||||
fn pm_type_deser<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<HashSet<diesel_models::enums::PaymentMethodType>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <String>::deserialize(deserializer)?;
|
||||
value
|
||||
.trim()
|
||||
.split(',')
|
||||
.map(diesel_models::enums::PaymentMethodType::from_str)
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct BankRedirectConfig(
|
||||
pub HashMap<api_models::enums::PaymentMethodType, ConnectorBankNames>,
|
||||
@ -363,7 +303,7 @@ pub struct ConnectorBankNames(pub HashMap<String, BanksVector>);
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct BanksVector {
|
||||
#[serde(deserialize_with = "bank_vec_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub banks: HashSet<api_models::enums::BankNames>,
|
||||
}
|
||||
|
||||
@ -385,9 +325,9 @@ pub enum PaymentMethodFilterKey {
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
#[serde(default)]
|
||||
pub struct CurrencyCountryFlowFilter {
|
||||
#[serde(deserialize_with = "currency_set_deser")]
|
||||
#[serde(deserialize_with = "deserialize_optional_hashset")]
|
||||
pub currency: Option<HashSet<api_models::enums::Currency>>,
|
||||
#[serde(deserialize_with = "string_set_deser")]
|
||||
#[serde(deserialize_with = "deserialize_optional_hashset")]
|
||||
pub country: Option<HashSet<api_models::enums::CountryAlpha2>>,
|
||||
pub not_available_flows: Option<NotAvailableFlows>,
|
||||
}
|
||||
@ -416,58 +356,6 @@ pub struct RequiredFieldFinal {
|
||||
pub common: HashMap<String, RequiredFieldInfo>,
|
||||
}
|
||||
|
||||
fn string_set_deser<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<HashSet<api_models::enums::CountryAlpha2>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <Option<String>>::deserialize(deserializer)?;
|
||||
Ok(value.and_then(|inner| {
|
||||
let list = inner
|
||||
.trim()
|
||||
.split(',')
|
||||
.flat_map(api_models::enums::CountryAlpha2::from_str)
|
||||
.collect::<HashSet<_>>();
|
||||
match list.len() {
|
||||
0 => None,
|
||||
_ => Some(list),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn currency_set_deser<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<HashSet<api_models::enums::Currency>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <Option<String>>::deserialize(deserializer)?;
|
||||
Ok(value.and_then(|inner| {
|
||||
let list = inner
|
||||
.trim()
|
||||
.split(',')
|
||||
.flat_map(api_models::enums::Currency::from_str)
|
||||
.collect::<HashSet<_>>();
|
||||
match list.len() {
|
||||
0 => None,
|
||||
_ => Some(list),
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn bank_vec_deser<'a, D>(deserializer: D) -> Result<HashSet<api_models::enums::BankNames>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <String>::deserialize(deserializer)?;
|
||||
Ok(value
|
||||
.trim()
|
||||
.split(',')
|
||||
.flat_map(api_models::enums::BankNames::from_str)
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Clone)]
|
||||
#[serde(default)]
|
||||
pub struct Secrets {
|
||||
@ -723,13 +611,13 @@ pub struct ApiKeys {
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct DelayedSessionConfig {
|
||||
#[serde(deserialize_with = "deser_to_get_connectors")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub connectors_with_delayed_session_response: HashSet<api_models::enums::Connector>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct WebhookSourceVerificationCall {
|
||||
#[serde(deserialize_with = "connector_deser")]
|
||||
#[serde(deserialize_with = "deserialize_hashset")]
|
||||
pub connectors_with_webhook_source_verification_call: HashSet<api_models::enums::Connector>,
|
||||
}
|
||||
|
||||
@ -746,21 +634,6 @@ pub struct ConnectorRequestReferenceIdConfig {
|
||||
pub merchant_ids_send_payment_id_as_connector_request_id: HashSet<String>,
|
||||
}
|
||||
|
||||
fn deser_to_get_connectors<'a, D>(
|
||||
deserializer: D,
|
||||
) -> Result<HashSet<api_models::enums::Connector>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
let value = <String>::deserialize(deserializer)?;
|
||||
value
|
||||
.trim()
|
||||
.split(',')
|
||||
.map(api_models::enums::Connector::from_str)
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(D::Error::custom)
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn new() -> ApplicationResult<Self> {
|
||||
Self::with_config_path(None)
|
||||
@ -854,24 +727,6 @@ impl Settings {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod payment_method_deserialization_test {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
use serde::de::{
|
||||
value::{Error as ValueError, StrDeserializer},
|
||||
IntoDeserializer,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_pm_deserializer() {
|
||||
let deserializer: StrDeserializer<'_, ValueError> = "wallet,card".into_deserializer();
|
||||
let test_pm = pm_deser(deserializer);
|
||||
assert!(test_pm.is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
pub struct Payouts {
|
||||
@ -886,7 +741,7 @@ pub struct LockSettings {
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LockSettings {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Inner {
|
||||
@ -921,3 +776,124 @@ pub struct PayPalOnboarding {
|
||||
pub partner_id: masking::Secret<String>,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
fn deserialize_hashset_inner<T>(value: impl AsRef<str>) -> Result<HashSet<T>, String>
|
||||
where
|
||||
T: Eq + std::str::FromStr + std::hash::Hash,
|
||||
<T as std::str::FromStr>::Err: std::fmt::Display,
|
||||
{
|
||||
let (values, errors) = value
|
||||
.as_ref()
|
||||
.trim()
|
||||
.split(',')
|
||||
.map(|s| {
|
||||
T::from_str(s.trim()).map_err(|error| {
|
||||
format!(
|
||||
"Unable to deserialize `{}` as `{}`: {error}",
|
||||
s.trim(),
|
||||
std::any::type_name::<T>()
|
||||
)
|
||||
})
|
||||
})
|
||||
.fold(
|
||||
(HashSet::new(), Vec::new()),
|
||||
|(mut values, mut errors), result| match result {
|
||||
Ok(t) => {
|
||||
values.insert(t);
|
||||
(values, errors)
|
||||
}
|
||||
Err(error) => {
|
||||
errors.push(error);
|
||||
(values, errors)
|
||||
}
|
||||
},
|
||||
);
|
||||
if !errors.is_empty() {
|
||||
Err(format!("Some errors occurred:\n{}", errors.join("\n")))
|
||||
} else {
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_hashset<'a, D, T>(deserializer: D) -> Result<HashSet<T>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'a>,
|
||||
T: Eq + std::str::FromStr + std::hash::Hash,
|
||||
<T as std::str::FromStr>::Err: std::fmt::Display,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
deserialize_hashset_inner(<String>::deserialize(deserializer)?).map_err(D::Error::custom)
|
||||
}
|
||||
|
||||
fn deserialize_optional_hashset<'a, D, T>(deserializer: D) -> Result<Option<HashSet<T>>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'a>,
|
||||
T: Eq + std::str::FromStr + std::hash::Hash,
|
||||
<T as std::str::FromStr>::Err: std::fmt::Display,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
<Option<String>>::deserialize(deserializer).map(|value| {
|
||||
value.map_or(Ok(None), |inner: String| {
|
||||
let list = deserialize_hashset_inner(inner).map_err(D::Error::custom)?;
|
||||
match list.len() {
|
||||
0 => Ok(None),
|
||||
_ => Ok(Some(list)),
|
||||
}
|
||||
})
|
||||
})?
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod hashset_deserialization_test {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::de::{
|
||||
value::{Error as ValueError, StrDeserializer},
|
||||
IntoDeserializer,
|
||||
};
|
||||
|
||||
use super::deserialize_hashset;
|
||||
|
||||
#[test]
|
||||
fn test_payment_method_hashset_deserializer() {
|
||||
use diesel_models::enums::PaymentMethod;
|
||||
|
||||
let deserializer: StrDeserializer<'_, ValueError> = "wallet,card".into_deserializer();
|
||||
let payment_methods = deserialize_hashset::<'_, _, PaymentMethod>(deserializer);
|
||||
let expected_payment_methods = HashSet::from([PaymentMethod::Wallet, PaymentMethod::Card]);
|
||||
|
||||
assert!(payment_methods.is_ok());
|
||||
assert_eq!(payment_methods.unwrap(), expected_payment_methods);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_payment_method_hashset_deserializer_with_spaces() {
|
||||
use diesel_models::enums::PaymentMethod;
|
||||
|
||||
let deserializer: StrDeserializer<'_, ValueError> =
|
||||
"wallet, card, bank_debit".into_deserializer();
|
||||
let payment_methods = deserialize_hashset::<'_, _, PaymentMethod>(deserializer);
|
||||
let expected_payment_methods = HashSet::from([
|
||||
PaymentMethod::Wallet,
|
||||
PaymentMethod::Card,
|
||||
PaymentMethod::BankDebit,
|
||||
]);
|
||||
|
||||
assert!(payment_methods.is_ok());
|
||||
assert_eq!(payment_methods.unwrap(), expected_payment_methods);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_payment_method_hashset_deserializer_error() {
|
||||
use diesel_models::enums::PaymentMethod;
|
||||
|
||||
let deserializer: StrDeserializer<'_, ValueError> =
|
||||
"wallet, card, unknown".into_deserializer();
|
||||
let payment_methods = deserialize_hashset::<'_, _, PaymentMethod>(deserializer);
|
||||
|
||||
assert!(payment_methods.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user