mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
refactor(config): change UCS connector list from array to comma-separated string (#8905)
This commit is contained in:
@ -1159,6 +1159,7 @@ url = "http://localhost:8080" # Open Router URL
|
|||||||
[grpc_client.unified_connector_service]
|
[grpc_client.unified_connector_service]
|
||||||
base_url = "http://localhost:8000" # Unified Connector Service Base URL
|
base_url = "http://localhost:8000" # Unified Connector Service Base URL
|
||||||
connection_timeout = 10 # Connection Timeout Duration in Seconds
|
connection_timeout = 10 # Connection Timeout Duration in Seconds
|
||||||
|
ucs_only_connectors = "paytm, phonepe" # Comma-separated list of connectors that use UCS only
|
||||||
|
|
||||||
[grpc_client.recovery_decider_client] # Revenue recovery client base url
|
[grpc_client.recovery_decider_client] # Revenue recovery client base url
|
||||||
base_url = "http://127.0.0.1:8080" #Base URL
|
base_url = "http://127.0.0.1:8080" #Base URL
|
||||||
|
|||||||
@ -384,6 +384,7 @@ connector_names = "connector_names" # Comma-separated list of allowed connec
|
|||||||
[grpc_client.unified_connector_service]
|
[grpc_client.unified_connector_service]
|
||||||
base_url = "http://localhost:8000" # Unified Connector Service Base URL
|
base_url = "http://localhost:8000" # Unified Connector Service Base URL
|
||||||
connection_timeout = 10 # Connection Timeout Duration in Seconds
|
connection_timeout = 10 # Connection Timeout Duration in Seconds
|
||||||
|
ucs_only_connectors = "paytm, phonepe" # Comma-separated list of connectors that use UCS only
|
||||||
|
|
||||||
[revenue_recovery.recovery_timestamp] # Timestamp configuration for Revenue Recovery
|
[revenue_recovery.recovery_timestamp] # Timestamp configuration for Revenue Recovery
|
||||||
initial_timestamp_in_hours = 1 # number of hours added to start time for Decider service of Revenue Recovery
|
initial_timestamp_in_hours = 1 # number of hours added to start time for Decider service of Revenue Recovery
|
||||||
|
|||||||
@ -837,3 +837,6 @@ click_to_pay = {connector_list = "adyen, cybersource, trustpay"}
|
|||||||
|
|
||||||
[list_dispute_supported_connectors]
|
[list_dispute_supported_connectors]
|
||||||
connector_list = "worldpayvantiv"
|
connector_list = "worldpayvantiv"
|
||||||
|
|
||||||
|
[grpc_client.unified_connector_service]
|
||||||
|
ucs_only_connectors = "paytm, phonepe" # Comma-separated list of connectors that use UCS only
|
||||||
|
|||||||
@ -847,3 +847,6 @@ click_to_pay = {connector_list = "adyen, cybersource, trustpay"}
|
|||||||
[revenue_recovery]
|
[revenue_recovery]
|
||||||
monitoring_threshold_in_seconds = 60
|
monitoring_threshold_in_seconds = 60
|
||||||
retry_algorithm_type = "cascading"
|
retry_algorithm_type = "cascading"
|
||||||
|
|
||||||
|
[grpc_client.unified_connector_service]
|
||||||
|
ucs_only_connectors = "paytm, phonepe" # Comma-separated list of connectors that use UCS only
|
||||||
|
|||||||
@ -856,3 +856,6 @@ retry_algorithm_type = "cascading"
|
|||||||
|
|
||||||
[list_dispute_supported_connectors]
|
[list_dispute_supported_connectors]
|
||||||
connector_list = "worldpayvantiv"
|
connector_list = "worldpayvantiv"
|
||||||
|
|
||||||
|
[grpc_client.unified_connector_service]
|
||||||
|
ucs_only_connectors = "paytm, phonepe" # Comma-separated list of connectors that use UCS only
|
||||||
|
|||||||
@ -1268,12 +1268,7 @@ enabled = "true"
|
|||||||
[grpc_client.unified_connector_service]
|
[grpc_client.unified_connector_service]
|
||||||
base_url = "http://localhost:8000"
|
base_url = "http://localhost:8000"
|
||||||
connection_timeout = 10
|
connection_timeout = 10
|
||||||
ucs_only_connectors = [
|
ucs_only_connectors = "paytm, phonepe" # Comma-separated list of connectors that use UCS only
|
||||||
"razorpay",
|
|
||||||
"phonepe",
|
|
||||||
"paytm",
|
|
||||||
"cashfree",
|
|
||||||
]
|
|
||||||
|
|
||||||
[revenue_recovery]
|
[revenue_recovery]
|
||||||
monitoring_threshold_in_seconds = 60
|
monitoring_threshold_in_seconds = 60
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use common_enums::connector_enums::Connector;
|
||||||
use common_utils::{consts as common_utils_consts, errors::CustomResult, types::Url};
|
use common_utils::{consts as common_utils_consts, errors::CustomResult, types::Url};
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
use masking::{PeekInterface, Secret};
|
use masking::{PeekInterface, Secret};
|
||||||
@ -18,6 +19,7 @@ use unified_connector_service_client::payments::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
consts,
|
consts,
|
||||||
grpc_client::{GrpcClientSettings, GrpcHeaders},
|
grpc_client::{GrpcClientSettings, GrpcHeaders},
|
||||||
|
utils::deserialize_hashset,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Unified Connector Service error variants
|
/// Unified Connector Service error variants
|
||||||
@ -123,9 +125,9 @@ pub struct UnifiedConnectorServiceClientConfig {
|
|||||||
/// Contains the connection timeout duration in seconds
|
/// Contains the connection timeout duration in seconds
|
||||||
pub connection_timeout: u64,
|
pub connection_timeout: u64,
|
||||||
|
|
||||||
/// List of connectors to use with the unified connector service
|
/// Set of external services/connectors available for the unified connector service
|
||||||
#[serde(default)]
|
#[serde(default, deserialize_with = "deserialize_hashset")]
|
||||||
pub ucs_only_connectors: Vec<String>,
|
pub ucs_only_connectors: HashSet<Connector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains the Connector Auth Type and related authentication data.
|
/// Contains the Connector Auth Type and related authentication data.
|
||||||
|
|||||||
@ -28,6 +28,9 @@ pub mod managers;
|
|||||||
/// crm module
|
/// crm module
|
||||||
pub mod crm;
|
pub mod crm;
|
||||||
|
|
||||||
|
/// deserializers module_path
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
#[cfg(feature = "revenue_recovery")]
|
#[cfg(feature = "revenue_recovery")]
|
||||||
/// date_time module
|
/// date_time module
|
||||||
pub mod date_time {
|
pub mod date_time {
|
||||||
|
|||||||
178
crates/external_services/src/utils.rs
Normal file
178
crates/external_services/src/utils.rs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
//! Custom deserializers for external services configuration
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
/// Parses a comma-separated string into a HashSet of typed values.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `value` - String or string reference containing comma-separated values
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(HashSet<T>)` - Successfully parsed HashSet
|
||||||
|
/// * `Err(String)` - Error message if any value parsing fails
|
||||||
|
///
|
||||||
|
/// # Type Parameters
|
||||||
|
///
|
||||||
|
/// * `T` - Target type that implements `FromStr`, `Eq`, and `Hash`
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::collections::HashSet;
|
||||||
|
///
|
||||||
|
/// let result: Result<HashSet<i32>, String> =
|
||||||
|
/// deserialize_hashset_inner("1,2,3");
|
||||||
|
/// assert!(result.is_ok());
|
||||||
|
///
|
||||||
|
/// if let Ok(hashset) = result {
|
||||||
|
/// assert!(hashset.contains(&1));
|
||||||
|
/// assert!(hashset.contains(&2));
|
||||||
|
/// assert!(hashset.contains(&3));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serde deserializer function for converting comma-separated strings into typed HashSets.
|
||||||
|
///
|
||||||
|
/// This function is designed to be used with serde's `#[serde(deserialize_with = "deserialize_hashset")]`
|
||||||
|
/// attribute to customize deserialization of HashSet fields.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `deserializer` - Serde deserializer instance
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(HashSet<T>)` - Successfully deserialized HashSet
|
||||||
|
/// * `Err(D::Error)` - Serde deserialization error
|
||||||
|
///
|
||||||
|
/// # Type Parameters
|
||||||
|
///
|
||||||
|
/// * `D` - Serde deserializer type
|
||||||
|
/// * `T` - Target type that implements `FromStr`, `Eq`, and `Hash`
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_hashset_inner_success() {
|
||||||
|
let result: Result<HashSet<i32>, String> = deserialize_hashset_inner("1,2,3");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Ok(hashset) = result {
|
||||||
|
assert_eq!(hashset.len(), 3);
|
||||||
|
assert!(hashset.contains(&1));
|
||||||
|
assert!(hashset.contains(&2));
|
||||||
|
assert!(hashset.contains(&3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_hashset_inner_with_whitespace() {
|
||||||
|
let result: Result<HashSet<String>, String> = deserialize_hashset_inner(" a , b , c ");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Ok(hashset) = result {
|
||||||
|
assert_eq!(hashset.len(), 3);
|
||||||
|
assert!(hashset.contains("a"));
|
||||||
|
assert!(hashset.contains("b"));
|
||||||
|
assert!(hashset.contains("c"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_hashset_inner_empty_string() {
|
||||||
|
let result: Result<HashSet<String>, String> = deserialize_hashset_inner("");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
if let Ok(hashset) = result {
|
||||||
|
assert_eq!(hashset.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_hashset_inner_single_value() {
|
||||||
|
let result: Result<HashSet<String>, String> = deserialize_hashset_inner("single");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Ok(hashset) = result {
|
||||||
|
assert_eq!(hashset.len(), 1);
|
||||||
|
assert!(hashset.contains("single"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_hashset_inner_invalid_int() {
|
||||||
|
let result: Result<HashSet<i32>, String> = deserialize_hashset_inner("1,invalid,3");
|
||||||
|
assert!(result.is_err());
|
||||||
|
|
||||||
|
if let Err(error) = result {
|
||||||
|
assert!(error.contains("Unable to deserialize `invalid` as `i32`"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deserialize_hashset_inner_duplicates() {
|
||||||
|
let result: Result<HashSet<String>, String> = deserialize_hashset_inner("a,b,a,c,b");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
if let Ok(hashset) = result {
|
||||||
|
assert_eq!(hashset.len(), 3); // Duplicates should be removed
|
||||||
|
assert!(hashset.contains("a"));
|
||||||
|
assert!(hashset.contains("b"));
|
||||||
|
assert!(hashset.contains("c"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use api_models::admin;
|
use api_models::admin;
|
||||||
use common_enums::{AttemptStatus, GatewaySystem, PaymentMethodType};
|
use common_enums::{connector_enums::Connector, AttemptStatus, GatewaySystem, PaymentMethodType};
|
||||||
use common_utils::{errors::CustomResult, ext_traits::ValueExt};
|
use common_utils::{errors::CustomResult, ext_traits::ValueExt};
|
||||||
use diesel_models::types::FeatureMetadata;
|
use diesel_models::types::FeatureMetadata;
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
@ -105,6 +107,9 @@ where
|
|||||||
.get_string_repr();
|
.get_string_repr();
|
||||||
|
|
||||||
let connector_name = router_data.connector.clone();
|
let connector_name = router_data.connector.clone();
|
||||||
|
let connector_enum = Connector::from_str(&connector_name)
|
||||||
|
.change_context(errors::ApiErrorResponse::IncorrectConnectorNameGiven)?;
|
||||||
|
|
||||||
let payment_method = router_data.payment_method.to_string();
|
let payment_method = router_data.payment_method.to_string();
|
||||||
let flow_name = get_flow_name::<F>()?;
|
let flow_name = get_flow_name::<F>()?;
|
||||||
|
|
||||||
@ -113,7 +118,7 @@ where
|
|||||||
.grpc_client
|
.grpc_client
|
||||||
.unified_connector_service
|
.unified_connector_service
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|config| config.ucs_only_connectors.contains(&connector_name));
|
.is_some_and(|config| config.ucs_only_connectors.contains(&connector_enum));
|
||||||
|
|
||||||
if is_ucs_only_connector {
|
if is_ucs_only_connector {
|
||||||
router_env::logger::info!(
|
router_env::logger::info!(
|
||||||
|
|||||||
Reference in New Issue
Block a user