mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 12:15:40 +08:00
feat(euclid): add dynamic routing in core flows (#6333)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -441,9 +441,13 @@ pub async fn link_routing_config(
|
||||
utils::when(
|
||||
matches!(
|
||||
dynamic_routing_ref.success_based_algorithm,
|
||||
Some(routing_types::DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: Some(ref id),
|
||||
timestamp: _
|
||||
Some(routing::SuccessBasedAlgorithm {
|
||||
algorithm_id_with_timestamp:
|
||||
routing_types::DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: Some(ref id),
|
||||
timestamp: _
|
||||
},
|
||||
enabled_feature: _
|
||||
}) if id == &algorithm_id
|
||||
),
|
||||
|| {
|
||||
@ -453,7 +457,17 @@ pub async fn link_routing_config(
|
||||
},
|
||||
)?;
|
||||
|
||||
dynamic_routing_ref.update_algorithm_id(algorithm_id);
|
||||
dynamic_routing_ref.update_algorithm_id(
|
||||
algorithm_id,
|
||||
dynamic_routing_ref
|
||||
.success_based_algorithm
|
||||
.clone()
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable(
|
||||
"missing success_based_algorithm in dynamic_algorithm_ref from business_profile table",
|
||||
)?
|
||||
.enabled_feature,
|
||||
);
|
||||
helpers::update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
key_manager_state,
|
||||
@ -1169,7 +1183,7 @@ pub async fn toggle_success_based_routing(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
status: bool,
|
||||
feature_to_enable: routing::SuccessBasedRoutingFeatures,
|
||||
profile_id: common_utils::id_type::ProfileId,
|
||||
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||
metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(
|
||||
@ -1205,115 +1219,158 @@ pub async fn toggle_success_based_routing(
|
||||
)?
|
||||
.unwrap_or_default();
|
||||
|
||||
if status {
|
||||
let default_success_based_routing_config = routing::SuccessBasedRoutingConfig::default();
|
||||
let algorithm_id = common_utils::generate_routing_id_of_default_length();
|
||||
let timestamp = common_utils::date_time::now();
|
||||
let algo = RoutingAlgorithm {
|
||||
algorithm_id: algorithm_id.clone(),
|
||||
profile_id: business_profile.get_id().to_owned(),
|
||||
merchant_id: merchant_account.get_id().to_owned(),
|
||||
name: "Dynamic routing algorithm".to_string(),
|
||||
description: None,
|
||||
kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
|
||||
algorithm_data: serde_json::json!(default_success_based_routing_config),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: common_enums::TransactionType::Payment,
|
||||
};
|
||||
match feature_to_enable {
|
||||
routing::SuccessBasedRoutingFeatures::Metrics
|
||||
| routing::SuccessBasedRoutingFeatures::DynamicConnectorSelection => {
|
||||
if let Some(ref mut algo_with_timestamp) =
|
||||
success_based_dynamic_routing_algo_ref.success_based_algorithm
|
||||
{
|
||||
match algo_with_timestamp
|
||||
.algorithm_id_with_timestamp
|
||||
.algorithm_id
|
||||
.clone()
|
||||
{
|
||||
Some(algorithm_id) => {
|
||||
// algorithm is already present in profile
|
||||
if algo_with_timestamp.enabled_feature == feature_to_enable {
|
||||
// algorithm already has the required feature
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Success rate based routing is already enabled"
|
||||
.to_string(),
|
||||
})?
|
||||
} else {
|
||||
// enable the requested feature for the algorithm
|
||||
algo_with_timestamp.update_enabled_features(feature_to_enable);
|
||||
let record = db
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(
|
||||
business_profile.get_id(),
|
||||
&algorithm_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(
|
||||
errors::ApiErrorResponse::ResourceIdNotFound,
|
||||
)?;
|
||||
let response = record.foreign_into();
|
||||
helpers::update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
business_profile,
|
||||
success_based_dynamic_routing_algo_ref,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let record = db
|
||||
.insert_routing_algorithm(algo)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Unable to insert record in routing algorithm table")?;
|
||||
|
||||
success_based_dynamic_routing_algo_ref.update_algorithm_id(algorithm_id);
|
||||
helpers::update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
business_profile,
|
||||
success_based_dynamic_routing_algo_ref,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let new_record = record.foreign_into();
|
||||
|
||||
metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([("profile_id", profile_id.get_string_repr().to_owned())]),
|
||||
);
|
||||
Ok(service_api::ApplicationResponse::Json(new_record))
|
||||
} else {
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
match success_based_dynamic_routing_algo_ref.success_based_algorithm {
|
||||
Some(algorithm_ref) => {
|
||||
if let Some(algorithm_id) = algorithm_ref.algorithm_id {
|
||||
let dynamic_routing_algorithm = routing_types::DynamicRoutingAlgorithmRef {
|
||||
success_based_algorithm: Some(
|
||||
routing_types::DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: None,
|
||||
timestamp,
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
// redact cache for success based routing configs
|
||||
let cache_key = format!(
|
||||
"{}_{}",
|
||||
business_profile.get_id().get_string_repr(),
|
||||
algorithm_id.get_string_repr()
|
||||
);
|
||||
let cache_entries_to_redact =
|
||||
vec![cache::CacheKind::SuccessBasedDynamicRoutingCache(
|
||||
cache_key.into(),
|
||||
)];
|
||||
let _ = cache::publish_into_redact_channel(
|
||||
state.store.get_cache_store().as_ref(),
|
||||
cache_entries_to_redact,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
logger::error!(
|
||||
"unable to publish into the redact channel for evicting the success based routing config cache {e:?}"
|
||||
)
|
||||
});
|
||||
|
||||
let record = db
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(
|
||||
business_profile.get_id(),
|
||||
&algorithm_id,
|
||||
metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([(
|
||||
"profile_id",
|
||||
profile_id.get_string_repr().to_owned(),
|
||||
)]),
|
||||
);
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// algorithm isn't present in profile
|
||||
helpers::default_success_based_routing_setup(
|
||||
&state,
|
||||
key_store,
|
||||
business_profile,
|
||||
feature_to_enable,
|
||||
merchant_account.get_id().to_owned(),
|
||||
success_based_dynamic_routing_algo_ref,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
let response = record.foreign_into();
|
||||
helpers::update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
business_profile,
|
||||
dynamic_routing_algorithm,
|
||||
)
|
||||
.await?;
|
||||
|
||||
metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([("profile_id", profile_id.get_string_repr().to_owned())]),
|
||||
);
|
||||
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
} else {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// algorithm isn't present in profile
|
||||
helpers::default_success_based_routing_setup(
|
||||
&state,
|
||||
key_store,
|
||||
business_profile,
|
||||
feature_to_enable,
|
||||
merchant_account.get_id().to_owned(),
|
||||
success_based_dynamic_routing_algo_ref,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
routing::SuccessBasedRoutingFeatures::None => {
|
||||
// disable success based routing for the requested profile
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
match success_based_dynamic_routing_algo_ref.success_based_algorithm {
|
||||
Some(algorithm_ref) => {
|
||||
if let Some(algorithm_id) =
|
||||
algorithm_ref.algorithm_id_with_timestamp.algorithm_id
|
||||
{
|
||||
let dynamic_routing_algorithm = routing_types::DynamicRoutingAlgorithmRef {
|
||||
success_based_algorithm: Some(routing::SuccessBasedAlgorithm {
|
||||
algorithm_id_with_timestamp:
|
||||
routing_types::DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: None,
|
||||
timestamp,
|
||||
},
|
||||
enabled_feature: routing::SuccessBasedRoutingFeatures::None,
|
||||
}),
|
||||
};
|
||||
|
||||
// redact cache for success based routing configs
|
||||
let cache_key = format!(
|
||||
"{}_{}",
|
||||
business_profile.get_id().get_string_repr(),
|
||||
algorithm_id.get_string_repr()
|
||||
);
|
||||
let cache_entries_to_redact =
|
||||
vec![cache::CacheKind::SuccessBasedDynamicRoutingCache(
|
||||
cache_key.into(),
|
||||
)];
|
||||
let _ = cache::publish_into_redact_channel(
|
||||
state.store.get_cache_store().as_ref(),
|
||||
cache_entries_to_redact,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("unable to publish into the redact channel for evicting the success based routing config cache")?;
|
||||
|
||||
let record = db
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(
|
||||
business_profile.get_id(),
|
||||
&algorithm_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
let response = record.foreign_into();
|
||||
helpers::update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
business_profile,
|
||||
dynamic_routing_algorithm,
|
||||
)
|
||||
.await?;
|
||||
|
||||
metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([(
|
||||
"profile_id",
|
||||
profile_id.get_string_repr().to_owned(),
|
||||
)]),
|
||||
);
|
||||
|
||||
Ok(service_api::ApplicationResponse::Json(response))
|
||||
} else {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})?
|
||||
}
|
||||
}
|
||||
None => Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Success rate based routing is already disabled".to_string(),
|
||||
})?,
|
||||
}
|
||||
None => Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user