feat(router): Create a merchant config for enable processor agnostic MIT (#4025)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Shankar Singh C
2024-04-03 20:30:07 +05:30
committed by GitHub
parent 49cfe72cd2
commit 2a691a5c05
7 changed files with 84 additions and 54 deletions

View File

@ -11,7 +11,7 @@ pub use euclid::{
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use crate::enums::{self, RoutableConnectors, TransactionType};
use crate::enums::{RoutableConnectors, TransactionType};
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
@ -299,34 +299,12 @@ impl From<RoutableConnectorChoice> for ast::ConnectorChoice {
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct DetailedConnectorChoice {
pub connector: RoutableConnectors,
pub business_label: Option<String>,
pub business_country: Option<enums::CountryAlpha2>,
pub business_sub_label: Option<String>,
pub enabled: bool,
}
impl DetailedConnectorChoice {
pub fn get_connector_label(&self) -> Option<String> {
self.business_country
.as_ref()
.zip(self.business_label.as_ref())
.map(|(business_country, business_label)| {
let mut base_label = format!(
"{}_{:?}_{}",
self.connector, business_country, business_label
);
if let Some(ref sub_label) = self.business_sub_label {
base_label.push('_');
base_label.push_str(sub_label);
}
base_label
})
}
}
impl common_utils::events::ApiEventMetric for DetailedConnectorChoice {}
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, strum::Display, ToSchema)]
#[serde(rename_all = "snake_case")]

View File

@ -9,7 +9,6 @@ use api_models::{
};
#[cfg(not(feature = "business_profile_routing"))]
use common_utils::ext_traits::{Encode, StringExt};
#[cfg(not(feature = "business_profile_routing"))]
use diesel_models::configs;
#[cfg(feature = "business_profile_routing")]
use diesel_models::routing_algorithm::RoutingAlgorithm;
@ -807,6 +806,37 @@ pub async fn retrieve_linked_routing_config(
}
}
pub async fn upsert_connector_agnostic_mandate_config(
state: AppState,
business_profile_id: &str,
mandate_config: routing_types::DetailedConnectorChoice,
) -> RouterResponse<routing_types::DetailedConnectorChoice> {
let key = helpers::get_pg_agnostic_mandate_config_key(business_profile_id);
let mandate_config_str = mandate_config.enabled.to_string();
let find_config = state
.store
.find_config_by_key_unwrap_or(&key, Some(mandate_config_str.clone()))
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("error saving pg agnostic mandate config to db")?;
if find_config.config != mandate_config_str {
let config_update = configs::ConfigUpdate::Update {
config: Some(mandate_config_str),
};
state
.store
.update_config_by_key(&key, config_update)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("error saving pg agnostic mandate config to db")?;
}
Ok(service_api::ApplicationResponse::Json(mandate_config))
}
pub async fn retrieve_default_routing_config_for_profiles(
state: AppState,
merchant_account: domain::MerchantAccount,

View File

@ -273,9 +273,9 @@ pub async fn update_business_profile_active_algorithm_ref(
pub async fn get_merchant_connector_agnostic_mandate_config(
db: &dyn StorageInterface,
merchant_id: &str,
business_profile_id: &str,
) -> RouterResult<Vec<routing_types::DetailedConnectorChoice>> {
let key = get_pg_agnostic_mandate_config_key(merchant_id);
let key = get_pg_agnostic_mandate_config_key(business_profile_id);
let maybe_config = db.find_config_by_key(&key).await;
match maybe_config {
@ -312,29 +312,6 @@ pub async fn get_merchant_connector_agnostic_mandate_config(
}
}
pub async fn update_merchant_connector_agnostic_mandate_config(
db: &dyn StorageInterface,
merchant_id: &str,
mandate_config: Vec<routing_types::DetailedConnectorChoice>,
) -> RouterResult<Vec<routing_types::DetailedConnectorChoice>> {
let key = get_pg_agnostic_mandate_config_key(merchant_id);
let mandate_config_str = mandate_config
.encode_to_string_of_json()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("unable to serialize pg agnostic mandate config during update")?;
let config_update = configs::ConfigUpdate::Update {
config: Some(mandate_config_str),
};
db.update_config_by_key(&key, config_update)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("error saving pg agnostic mandate config to db")?;
Ok(mandate_config)
}
pub async fn validate_connectors_in_routing_config(
db: &dyn StorageInterface,
key_store: &domain::MerchantKeyStore,
@ -465,8 +442,8 @@ pub fn get_routing_dictionary_key(merchant_id: &str) -> String {
/// Provides the identifier for the specific merchant's agnostic_mandate_config
#[inline(always)]
pub fn get_pg_agnostic_mandate_config_key(merchant_id: &str) -> String {
format!("pg_agnostic_mandate_{merchant_id}")
pub fn get_pg_agnostic_mandate_config_key(business_profile_id: &str) -> String {
format!("pg_agnostic_mandate_{business_profile_id}")
}
/// Provides the identifier for the specific merchant's default_config

View File

@ -437,6 +437,10 @@ impl Routing {
)
})),
)
.service(
web::resource("/business_profile/{business_profile_id}/configs/pg_agnostic_mit")
.route(web::post().to(cloud_routing::upsert_connector_agnostic_mandate_config)),
)
.service(
web::resource("/default")
.route(web::get().to(|state, req| {

View File

@ -225,6 +225,7 @@ impl From<Flow> for ApiIdentifier {
| Flow::ReconTokenRequest
| Flow::ReconServiceRequest
| Flow::ReconVerifyToken => Self::Recon,
Flow::CreateConnectorAgnosticMandateConfig => Self::Routing,
}
}
}

View File

@ -566,6 +566,44 @@ pub async fn routing_retrieve_linked_config(
}
}
#[cfg(feature = "olap")]
#[instrument(skip_all)]
pub async fn upsert_connector_agnostic_mandate_config(
state: web::Data<AppState>,
req: HttpRequest,
json_payload: web::Json<routing_types::DetailedConnectorChoice>,
path: web::Path<String>,
) -> impl Responder {
use crate::services::authentication::AuthenticationData;
let flow = Flow::CreateConnectorAgnosticMandateConfig;
let business_profile_id = path.into_inner();
Box::pin(oss_api::server_wrap(
flow,
state,
&req,
json_payload.into_inner(),
|state, _auth: AuthenticationData, mandate_config| {
Box::pin(routing::upsert_connector_agnostic_mandate_config(
state,
&business_profile_id,
mandate_config,
))
},
#[cfg(not(feature = "release"))]
auth::auth_type(
&auth::ApiKeyAuth,
&auth::JWTAuth(Permission::RoutingWrite),
req.headers(),
),
#[cfg(feature = "release")]
&auth::JWTAuth(Permission::RoutingWrite),
api_locking::LockAction::NotApplicable,
))
.await
}
#[cfg(feature = "olap")]
#[instrument(skip_all)]
pub async fn routing_retrieve_default_config_for_profiles(

View File

@ -206,6 +206,8 @@ pub enum Flow {
RoutingRetrieveConfig,
/// Routing retrieve active config
RoutingRetrieveActiveConfig,
/// Update connector agnostic mandate config
CreateConnectorAgnosticMandateConfig,
/// Routing retrieve default config
RoutingRetrieveDefaultConfig,
/// Routing retrieve dictionary