mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat: migration api for migrating routing rules to decision_engine (#8233)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -6,7 +6,9 @@ use std::collections::HashSet;
|
||||
use api_models::routing::DynamicRoutingAlgoAccessor;
|
||||
use api_models::{
|
||||
enums, mandates as mandates_api, routing,
|
||||
routing::{self as routing_types, RoutingRetrieveQuery},
|
||||
routing::{
|
||||
self as routing_types, RoutingRetrieveQuery, RuleMigrationError, RuleMigrationResponse,
|
||||
},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||
@ -22,6 +24,7 @@ use external_services::grpc_client::dynamic_routing::{
|
||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||
use helpers::update_decision_engine_dynamic_routing_setup;
|
||||
use hyperswitch_domain_models::{mandates, payment_address};
|
||||
use payment_methods::helpers::StorageErrorExt;
|
||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||
use router_env::logger;
|
||||
use rustc_hash::FxHashSet;
|
||||
@ -46,7 +49,7 @@ use crate::utils::ValueExt;
|
||||
use crate::{core::admin, utils::ValueExt};
|
||||
use crate::{
|
||||
core::{
|
||||
errors::{self, CustomResult, RouterResponse, StorageErrorExt},
|
||||
errors::{self, CustomResult, RouterResponse},
|
||||
metrics, utils as core_utils,
|
||||
},
|
||||
db::StorageInterface,
|
||||
@ -345,6 +348,7 @@ pub async fn create_routing_algorithm_under_profile(
|
||||
match program.try_into() {
|
||||
Ok(internal_program) => {
|
||||
let routing_rule = RoutingRule {
|
||||
rule_id: None,
|
||||
name: name.clone(),
|
||||
description: Some(description.clone()),
|
||||
created_by: profile_id.get_string_repr().to_string(),
|
||||
@ -2291,3 +2295,158 @@ impl RoutableConnectors {
|
||||
Ok(connector_data)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn migrate_rules_for_profile(
|
||||
state: SessionState,
|
||||
merchant_context: domain::MerchantContext,
|
||||
query_params: routing_types::RuleMigrationQuery,
|
||||
) -> RouterResult<routing_types::RuleMigrationResult> {
|
||||
use api_models::routing::StaticRoutingAlgorithm as EuclidAlgorithm;
|
||||
|
||||
use crate::services::logger;
|
||||
|
||||
let profile_id = query_params.profile_id.clone();
|
||||
let db = state.store.as_ref();
|
||||
let key_manager_state = &(&state).into();
|
||||
let merchant_key_store = merchant_context.get_merchant_key_store();
|
||||
let merchant_id = merchant_context.get_merchant_account().get_id();
|
||||
|
||||
core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
key_manager_state,
|
||||
merchant_key_store,
|
||||
Some(&profile_id),
|
||||
merchant_id,
|
||||
)
|
||||
.await?
|
||||
.get_required_value("Profile")
|
||||
.change_context(errors::ApiErrorResponse::ProfileNotFound {
|
||||
id: profile_id.get_string_repr().to_owned(),
|
||||
})?;
|
||||
|
||||
let routing_metadatas: Vec<diesel_models::routing_algorithm::RoutingProfileMetadata> = state
|
||||
.store
|
||||
.list_routing_algorithm_metadata_by_profile_id(
|
||||
&profile_id,
|
||||
i64::from(query_params.validated_limit()),
|
||||
i64::from(query_params.offset.unwrap_or_default()),
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
|
||||
let mut response_list = Vec::new();
|
||||
let mut error_list = Vec::new();
|
||||
|
||||
for routing_metadata in routing_metadatas
|
||||
.into_iter()
|
||||
.filter(|algo| algo.metadata_is_advanced_rule_for_payments())
|
||||
{
|
||||
match db
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(
|
||||
&profile_id,
|
||||
&routing_metadata.algorithm_id,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(algorithm) => {
|
||||
let parsed_result = algorithm
|
||||
.algorithm_data
|
||||
.parse_value::<EuclidAlgorithm>("EuclidAlgorithm");
|
||||
|
||||
match parsed_result {
|
||||
Ok(EuclidAlgorithm::Advanced(program)) => match program.try_into() {
|
||||
Ok(internal_program) => {
|
||||
let routing_rule = RoutingRule {
|
||||
rule_id: Some(
|
||||
algorithm.algorithm_id.clone().get_string_repr().to_string(),
|
||||
),
|
||||
name: algorithm.name.clone(),
|
||||
description: algorithm.description.clone(),
|
||||
created_by: profile_id.get_string_repr().to_string(),
|
||||
algorithm: internal_program,
|
||||
metadata: None,
|
||||
};
|
||||
|
||||
let result = create_de_euclid_routing_algo(&state, &routing_rule).await;
|
||||
|
||||
match result {
|
||||
Ok(decision_engine_routing_id) => {
|
||||
let response = RuleMigrationResponse {
|
||||
profile_id: profile_id.clone(),
|
||||
euclid_algorithm_id: algorithm.algorithm_id.clone(),
|
||||
decision_engine_algorithm_id: decision_engine_routing_id,
|
||||
};
|
||||
response_list.push(response);
|
||||
}
|
||||
Err(err) => {
|
||||
logger::error!(
|
||||
decision_engine_rule_migration_error = ?err,
|
||||
algorithm_id = ?algorithm.algorithm_id,
|
||||
"Failed to insert into decision engine"
|
||||
);
|
||||
error_list.push(RuleMigrationError {
|
||||
profile_id: profile_id.clone(),
|
||||
algorithm_id: algorithm.algorithm_id.clone(),
|
||||
error: format!("Insertion error: {:?}", err),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
logger::error!(
|
||||
decision_engine_rule_migration_error = ?e,
|
||||
algorithm_id = ?algorithm.algorithm_id,
|
||||
"Failed to convert program"
|
||||
);
|
||||
error_list.push(RuleMigrationError {
|
||||
profile_id: profile_id.clone(),
|
||||
algorithm_id: algorithm.algorithm_id.clone(),
|
||||
error: format!("Program conversion error: {:?}", e),
|
||||
});
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
logger::error!(
|
||||
decision_engine_rule_migration_error = ?e,
|
||||
algorithm_id = ?algorithm.algorithm_id,
|
||||
"Failed to parse EuclidAlgorithm"
|
||||
);
|
||||
error_list.push(RuleMigrationError {
|
||||
profile_id: profile_id.clone(),
|
||||
algorithm_id: algorithm.algorithm_id.clone(),
|
||||
error: format!("JSON parse error: {:?}", e),
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
logger::info!(
|
||||
"decision_engine_rule_migration_error: Skipping non-advanced algorithm {:?}",
|
||||
algorithm.algorithm_id
|
||||
);
|
||||
error_list.push(RuleMigrationError {
|
||||
profile_id: profile_id.clone(),
|
||||
algorithm_id: algorithm.algorithm_id.clone(),
|
||||
error: "Not an advanced algorithm".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
logger::error!(
|
||||
decision_engine_rule_migration_error = ?e,
|
||||
algorithm_id = ?routing_metadata.algorithm_id,
|
||||
"Failed to fetch routing algorithm"
|
||||
);
|
||||
error_list.push(RuleMigrationError {
|
||||
profile_id: profile_id.clone(),
|
||||
algorithm_id: routing_metadata.algorithm_id.clone(),
|
||||
error: format!("Fetch error: {:?}", e),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(routing_types::RuleMigrationResult {
|
||||
success: response_list,
|
||||
errors: error_list,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user