feat(euclid): integration with decision engine (#7930)

Co-authored-by: Jagan Elavarasan <jaganelavarasan@gmail.com>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Prajjwal Kumar
2025-05-14 23:38:59 +05:30
committed by GitHub
parent c2ad04f4a0
commit 4087cd276e
13 changed files with 882 additions and 8 deletions

View File

@ -31,7 +31,10 @@ use super::payouts;
use super::{
errors::RouterResult,
payments::{
routing::{self as payments_routing},
routing::{
utils::*,
{self as payments_routing},
},
OperationSessionGetters,
},
};
@ -118,6 +121,7 @@ impl RoutingAlgorithmUpdate {
created_at: timestamp,
modified_at: timestamp,
algorithm_for: transaction_type,
decision_engine_routing_id: None,
};
Self(algo)
}
@ -153,14 +157,34 @@ pub async fn retrieve_merchant_routing_dictionary(
)
.await
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
let routing_metadata =
super::utils::filter_objects_based_on_profile_id_list(profile_id_list, routing_metadata);
let routing_metadata = super::utils::filter_objects_based_on_profile_id_list(
profile_id_list.clone(),
routing_metadata,
);
let result = routing_metadata
.into_iter()
.map(ForeignInto::foreign_into)
.collect::<Vec<_>>();
if let Some(profile_ids) = profile_id_list {
let mut de_result: Vec<routing_types::RoutingDictionaryRecord> = vec![];
// DE_TODO: need to replace this with batch API call to reduce the number of network calls
for profile_id in profile_ids {
let list_request = ListRountingAlgorithmsRequest {
created_by: profile_id.get_string_repr().to_string(),
};
list_de_euclid_routing_algorithms(&state, list_request)
.await
.map_err(|e| {
router_env::logger::error!(decision_engine_error=?e, "decision_engine_euclid");
})
.ok() // Avoid throwing error if Decision Engine is not available or other errors
.map(|mut de_routing| de_result.append(&mut de_routing));
}
compare_and_log_result(de_result, result.clone(), "list_routing".to_string());
}
metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE.add(1, &[]);
Ok(service_api::ApplicationResponse::Json(
routing_types::RoutingKind::RoutingAlgorithm(result),
@ -249,6 +273,10 @@ pub async fn create_routing_algorithm_under_profile(
request: routing_types::RoutingConfigRequest,
transaction_type: enums::TransactionType,
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
use api_models::routing::RoutingAlgorithm as EuclidAlgorithm;
use crate::services::logger;
metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(1, &[]);
let db = state.store.as_ref();
let key_manager_state = &(&state).into();
@ -269,6 +297,7 @@ pub async fn create_routing_algorithm_under_profile(
let algorithm = request
.algorithm
.clone()
.get_required_value("algorithm")
.change_context(errors::ApiErrorResponse::MissingRequiredField {
field_name: "algorithm",
@ -306,6 +335,37 @@ pub async fn create_routing_algorithm_under_profile(
)
.await?;
let mut decision_engine_routing_id: Option<String> = None;
if let Some(EuclidAlgorithm::Advanced(program)) = request.algorithm.clone() {
let internal_program: Program = program.into();
let routing_rule = RoutingRule {
name: name.clone(),
description: Some(description.clone()),
created_by: profile_id.get_string_repr().to_string(),
algorithm: internal_program,
metadata: Some(RoutingMetadata {
kind: algorithm.get_kind().foreign_into(),
algorithm_for: transaction_type.to_owned(),
}),
};
decision_engine_routing_id = create_de_euclid_routing_algo(&state, &routing_rule)
.await
.map_err(|e| {
// errors are ignored as this is just for diff checking as of now (optional flow).
logger::error!(decision_engine_error=?e, "decision_engine_euclid");
logger::debug!(decision_engine_request=?routing_rule, "decision_engine_euclid");
})
.ok();
}
if decision_engine_routing_id.is_some() {
logger::info!(routing_flow=?"create_euclid_routing_algorithm", is_equal=?"true", "decision_engine_euclid");
} else {
logger::info!(routing_flow=?"create_euclid_routing_algorithm", is_equal=?"false", "decision_engine_euclid");
}
let timestamp = common_utils::date_time::now();
let algo = RoutingAlgorithm {
algorithm_id: algorithm_id.clone(),
@ -318,6 +378,7 @@ pub async fn create_routing_algorithm_under_profile(
created_at: timestamp,
modified_at: timestamp,
algorithm_for: transaction_type.to_owned(),
decision_engine_routing_id,
};
let record = db
.insert_routing_algorithm(algo)
@ -536,7 +597,7 @@ pub async fn link_routing_config(
db,
key_manager_state,
merchant_context.get_merchant_key_store(),
business_profile,
business_profile.clone(),
dynamic_routing_ref,
)
.await?;
@ -578,14 +639,37 @@ pub async fn link_routing_config(
db,
key_manager_state,
merchant_context.get_merchant_key_store(),
business_profile,
business_profile.clone(),
routing_ref,
transaction_type,
)
.await?;
}
};
if let Some(euclid_routing_id) = routing_algorithm.decision_engine_routing_id.clone() {
let routing_algo = ActivateRoutingConfigRequest {
created_by: business_profile.get_id().get_string_repr().to_string(),
routing_algorithm_id: euclid_routing_id,
};
let link_result = link_de_euclid_routing_algorithm(&state, routing_algo).await;
match link_result {
Ok(_) => {
router_env::logger::info!(
routing_flow=?"link_routing_algorithm",
is_equal=?true,
"decision_engine_euclid"
);
}
Err(e) => {
router_env::logger::info!(
routing_flow=?"link_routing_algorithm",
is_equal=?false,
error=?e,
"decision_engine_euclid"
);
}
}
}
metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(1, &[]);
Ok(service_api::ApplicationResponse::Json(
routing_algorithm.foreign_into(),
@ -1436,6 +1520,7 @@ pub async fn success_based_routing_update_configs(
created_at: timestamp,
modified_at: timestamp,
algorithm_for: dynamic_routing_algo_to_update.algorithm_for,
decision_engine_routing_id: None,
};
let record = db
.insert_routing_algorithm(algo)
@ -1535,6 +1620,7 @@ pub async fn elimination_routing_update_configs(
created_at: timestamp,
modified_at: timestamp,
algorithm_for: dynamic_routing_algo_to_update.algorithm_for,
decision_engine_routing_id: None,
};
let record = db
@ -1680,6 +1766,7 @@ pub async fn contract_based_dynamic_routing_setup(
created_at: timestamp,
modified_at: timestamp,
algorithm_for: common_enums::TransactionType::Payment,
decision_engine_routing_id: None,
};
// 1. if dynamic_routing_algo_ref already present, insert contract based algo and disable success based
@ -1867,6 +1954,7 @@ pub async fn contract_based_routing_update_configs(
created_at: timestamp,
modified_at: timestamp,
algorithm_for: dynamic_routing_algo_to_update.algorithm_for,
decision_engine_routing_id: None,
};
let record = db
.insert_routing_algorithm(algo)