mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
feat: added create endpoint for dynamic_routing (#8755)
Co-authored-by: Ankit Gupta <ankit.gupta.001@Ankit-Gupta-M6QG9L6RHV.local> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Jagan <jaganelavarasan@gmail.com>
This commit is contained in:
@ -2,7 +2,7 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};
|
|||||||
|
|
||||||
use crate::routing::{
|
use crate::routing::{
|
||||||
ContractBasedRoutingPayloadWrapper, ContractBasedRoutingSetupPayloadWrapper,
|
ContractBasedRoutingPayloadWrapper, ContractBasedRoutingSetupPayloadWrapper,
|
||||||
DynamicRoutingUpdateConfigQuery, EliminationRoutingPayloadWrapper,
|
CreateDynamicRoutingWrapper, DynamicRoutingUpdateConfigQuery, EliminationRoutingPayloadWrapper,
|
||||||
LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig,
|
LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig,
|
||||||
RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind,
|
RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind,
|
||||||
RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery,
|
RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery,
|
||||||
@ -125,6 +125,12 @@ impl ApiEventMetric for ToggleDynamicRoutingWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ApiEventMetric for CreateDynamicRoutingWrapper {
|
||||||
|
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||||
|
Some(ApiEventsType::Routing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ApiEventMetric for DynamicRoutingUpdateConfigQuery {
|
impl ApiEventMetric for DynamicRoutingUpdateConfigQuery {
|
||||||
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||||
Some(ApiEventsType::Routing)
|
Some(ApiEventsType::Routing)
|
||||||
|
|||||||
@ -836,6 +836,33 @@ impl DynamicRoutingAlgorithmRef {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_feature(
|
||||||
|
&mut self,
|
||||||
|
enabled_feature: DynamicRoutingFeatures,
|
||||||
|
dynamic_routing_type: DynamicRoutingType,
|
||||||
|
) {
|
||||||
|
match dynamic_routing_type {
|
||||||
|
DynamicRoutingType::SuccessRateBasedRouting => {
|
||||||
|
self.success_based_algorithm = Some(SuccessBasedAlgorithm {
|
||||||
|
algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
|
||||||
|
enabled_feature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
DynamicRoutingType::EliminationRouting => {
|
||||||
|
self.elimination_routing_algorithm = Some(EliminationRoutingAlgorithm {
|
||||||
|
algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
|
||||||
|
enabled_feature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
DynamicRoutingType::ContractBasedRouting => {
|
||||||
|
self.contract_based_routing = Some(ContractRoutingAlgorithm {
|
||||||
|
algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp::new(None),
|
||||||
|
enabled_feature,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disable_algorithm_id(&mut self, dynamic_routing_type: DynamicRoutingType) {
|
pub fn disable_algorithm_id(&mut self, dynamic_routing_type: DynamicRoutingType) {
|
||||||
match dynamic_routing_type {
|
match dynamic_routing_type {
|
||||||
DynamicRoutingType::SuccessRateBasedRouting => {
|
DynamicRoutingType::SuccessRateBasedRouting => {
|
||||||
@ -871,6 +898,11 @@ pub struct ToggleDynamicRoutingQuery {
|
|||||||
pub enable: DynamicRoutingFeatures,
|
pub enable: DynamicRoutingFeatures,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
|
pub struct CreateDynamicRoutingQuery {
|
||||||
|
pub enable: DynamicRoutingFeatures,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, ToSchema)]
|
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
pub struct DynamicRoutingVolumeSplitQuery {
|
pub struct DynamicRoutingVolumeSplitQuery {
|
||||||
pub split: u8,
|
pub split: u8,
|
||||||
@ -907,6 +939,20 @@ pub struct ToggleDynamicRoutingPath {
|
|||||||
pub profile_id: common_utils::id_type::ProfileId,
|
pub profile_id: common_utils::id_type::ProfileId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct CreateDynamicRoutingWrapper {
|
||||||
|
pub profile_id: common_utils::id_type::ProfileId,
|
||||||
|
pub feature_to_enable: DynamicRoutingFeatures,
|
||||||
|
pub payload: DynamicRoutingPayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
|
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||||
|
pub enum DynamicRoutingPayload {
|
||||||
|
SuccessBasedRoutingPayload(SuccessBasedRoutingConfig),
|
||||||
|
EliminationRoutingPayload(EliminationRoutingConfig),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||||
pub struct RoutingVolumeSplitResponse {
|
pub struct RoutingVolumeSplitResponse {
|
||||||
pub split: u8,
|
pub split: u8,
|
||||||
|
|||||||
@ -1598,6 +1598,7 @@ pub async fn update_default_routing_config_for_profile(
|
|||||||
|
|
||||||
// Toggle the specific routing type as well as add the default configs in RoutingAlgorithm table
|
// Toggle the specific routing type as well as add the default configs in RoutingAlgorithm table
|
||||||
// and update the same in business profile table.
|
// and update the same in business profile table.
|
||||||
|
|
||||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||||
pub async fn toggle_specific_dynamic_routing(
|
pub async fn toggle_specific_dynamic_routing(
|
||||||
state: SessionState,
|
state: SessionState,
|
||||||
@ -1647,16 +1648,88 @@ pub async fn toggle_specific_dynamic_routing(
|
|||||||
// 1. If present with same feature then return response as already enabled
|
// 1. If present with same feature then return response as already enabled
|
||||||
// 2. Else update the feature and persist the same on db
|
// 2. Else update the feature and persist the same on db
|
||||||
// 3. If not present in db then create a new default entry
|
// 3. If not present in db then create a new default entry
|
||||||
helpers::enable_dynamic_routing_algorithm(
|
Box::pin(helpers::enable_dynamic_routing_algorithm(
|
||||||
&state,
|
&state,
|
||||||
merchant_context.get_merchant_key_store().clone(),
|
merchant_context.get_merchant_key_store().clone(),
|
||||||
business_profile,
|
business_profile,
|
||||||
feature_to_enable,
|
feature_to_enable,
|
||||||
dynamic_routing_algo_ref,
|
dynamic_routing_algo_ref,
|
||||||
dynamic_routing_type,
|
dynamic_routing_type,
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
routing::DynamicRoutingFeatures::None => {
|
||||||
|
// disable specific dynamic routing for the requested profile
|
||||||
|
helpers::disable_dynamic_routing_algorithm(
|
||||||
|
&state,
|
||||||
|
merchant_context.get_merchant_key_store().clone(),
|
||||||
|
business_profile,
|
||||||
|
dynamic_routing_algo_ref,
|
||||||
|
dynamic_routing_type,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||||
|
pub async fn create_specific_dynamic_routing(
|
||||||
|
state: SessionState,
|
||||||
|
merchant_context: domain::MerchantContext,
|
||||||
|
feature_to_enable: routing::DynamicRoutingFeatures,
|
||||||
|
profile_id: common_utils::id_type::ProfileId,
|
||||||
|
dynamic_routing_type: routing::DynamicRoutingType,
|
||||||
|
payload: routing_types::DynamicRoutingPayload,
|
||||||
|
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||||
|
metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(
|
||||||
|
1,
|
||||||
|
router_env::metric_attributes!(
|
||||||
|
("profile_id", profile_id.clone()),
|
||||||
|
("algorithm_type", dynamic_routing_type.to_string())
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let db = state.store.as_ref();
|
||||||
|
let key_manager_state = &(&state).into();
|
||||||
|
|
||||||
|
let business_profile: domain::Profile = core_utils::validate_and_get_business_profile(
|
||||||
|
db,
|
||||||
|
key_manager_state,
|
||||||
|
merchant_context.get_merchant_key_store(),
|
||||||
|
Some(&profile_id),
|
||||||
|
merchant_context.get_merchant_account().get_id(),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.get_required_value("Profile")
|
||||||
|
.change_context(errors::ApiErrorResponse::ProfileNotFound {
|
||||||
|
id: profile_id.get_string_repr().to_owned(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef = business_profile
|
||||||
|
.dynamic_routing_algorithm
|
||||||
|
.clone()
|
||||||
|
.map(|val| val.parse_value("DynamicRoutingAlgorithmRef"))
|
||||||
|
.transpose()
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable(
|
||||||
|
"unable to deserialize dynamic routing algorithm ref from business profile",
|
||||||
|
)?
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
match feature_to_enable {
|
||||||
|
routing::DynamicRoutingFeatures::Metrics
|
||||||
|
| routing::DynamicRoutingFeatures::DynamicConnectorSelection => {
|
||||||
|
Box::pin(helpers::enable_dynamic_routing_algorithm(
|
||||||
|
&state,
|
||||||
|
merchant_context.get_merchant_key_store().clone(),
|
||||||
|
business_profile,
|
||||||
|
feature_to_enable,
|
||||||
|
dynamic_routing_algo_ref,
|
||||||
|
dynamic_routing_type,
|
||||||
|
Some(payload),
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
routing::DynamicRoutingFeatures::None => {
|
routing::DynamicRoutingFeatures::None => {
|
||||||
// disable specific dynamic routing for the requested profile
|
// disable specific dynamic routing for the requested profile
|
||||||
helpers::disable_dynamic_routing_algorithm(
|
helpers::disable_dynamic_routing_algorithm(
|
||||||
|
|||||||
@ -1979,6 +1979,7 @@ pub async fn enable_dynamic_routing_algorithm(
|
|||||||
feature_to_enable: routing_types::DynamicRoutingFeatures,
|
feature_to_enable: routing_types::DynamicRoutingFeatures,
|
||||||
dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
|
dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
|
||||||
dynamic_routing_type: routing_types::DynamicRoutingType,
|
dynamic_routing_type: routing_types::DynamicRoutingType,
|
||||||
|
payload: Option<routing_types::DynamicRoutingPayload>,
|
||||||
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
|
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
|
||||||
let mut dynamic_routing = dynamic_routing_algo_ref.clone();
|
let mut dynamic_routing = dynamic_routing_algo_ref.clone();
|
||||||
match dynamic_routing_type {
|
match dynamic_routing_type {
|
||||||
@ -1994,6 +1995,7 @@ pub async fn enable_dynamic_routing_algorithm(
|
|||||||
dynamic_routing.clone(),
|
dynamic_routing.clone(),
|
||||||
dynamic_routing_type,
|
dynamic_routing_type,
|
||||||
dynamic_routing.success_based_algorithm,
|
dynamic_routing.success_based_algorithm,
|
||||||
|
payload,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -2006,6 +2008,7 @@ pub async fn enable_dynamic_routing_algorithm(
|
|||||||
dynamic_routing.clone(),
|
dynamic_routing.clone(),
|
||||||
dynamic_routing_type,
|
dynamic_routing_type,
|
||||||
dynamic_routing.elimination_routing_algorithm,
|
dynamic_routing.elimination_routing_algorithm,
|
||||||
|
payload,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -2018,6 +2021,7 @@ pub async fn enable_dynamic_routing_algorithm(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||||
pub async fn enable_specific_routing_algorithm<A>(
|
pub async fn enable_specific_routing_algorithm<A>(
|
||||||
state: &SessionState,
|
state: &SessionState,
|
||||||
@ -2027,10 +2031,24 @@ pub async fn enable_specific_routing_algorithm<A>(
|
|||||||
mut dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
|
mut dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
|
||||||
dynamic_routing_type: routing_types::DynamicRoutingType,
|
dynamic_routing_type: routing_types::DynamicRoutingType,
|
||||||
algo_type: Option<A>,
|
algo_type: Option<A>,
|
||||||
|
payload: Option<routing_types::DynamicRoutingPayload>,
|
||||||
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>>
|
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>>
|
||||||
where
|
where
|
||||||
A: routing_types::DynamicRoutingAlgoAccessor + Clone + Debug,
|
A: routing_types::DynamicRoutingAlgoAccessor + Clone + Debug,
|
||||||
{
|
{
|
||||||
|
//Check for payload
|
||||||
|
if let Some(payload) = payload {
|
||||||
|
return create_specific_dynamic_routing_setup(
|
||||||
|
state,
|
||||||
|
key_store,
|
||||||
|
business_profile,
|
||||||
|
feature_to_enable,
|
||||||
|
dynamic_routing_algo_ref,
|
||||||
|
dynamic_routing_type,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
// Algorithm wasn't created yet
|
// Algorithm wasn't created yet
|
||||||
let Some(mut algo_type) = algo_type else {
|
let Some(mut algo_type) = algo_type else {
|
||||||
return default_specific_dynamic_routing_setup(
|
return default_specific_dynamic_routing_setup(
|
||||||
@ -2112,6 +2130,7 @@ pub async fn default_specific_dynamic_routing_setup(
|
|||||||
let merchant_id = business_profile.merchant_id.clone();
|
let merchant_id = business_profile.merchant_id.clone();
|
||||||
let algorithm_id = common_utils::generate_routing_id_of_default_length();
|
let algorithm_id = common_utils::generate_routing_id_of_default_length();
|
||||||
let timestamp = common_utils::date_time::now();
|
let timestamp = common_utils::date_time::now();
|
||||||
|
|
||||||
let algo = match dynamic_routing_type {
|
let algo = match dynamic_routing_type {
|
||||||
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
|
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
|
||||||
let default_success_based_routing_config =
|
let default_success_based_routing_config =
|
||||||
@ -2142,6 +2161,7 @@ pub async fn default_specific_dynamic_routing_setup(
|
|||||||
} else {
|
} else {
|
||||||
routing_types::EliminationRoutingConfig::default()
|
routing_types::EliminationRoutingConfig::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
routing_algorithm::RoutingAlgorithm {
|
routing_algorithm::RoutingAlgorithm {
|
||||||
algorithm_id: algorithm_id.clone(),
|
algorithm_id: algorithm_id.clone(),
|
||||||
profile_id: profile_id.clone(),
|
profile_id: profile_id.clone(),
|
||||||
@ -2173,6 +2193,7 @@ pub async fn default_specific_dynamic_routing_setup(
|
|||||||
business_profile.get_id(),
|
business_profile.get_id(),
|
||||||
dynamic_routing_type,
|
dynamic_routing_type,
|
||||||
&mut dynamic_routing_algo_ref,
|
&mut dynamic_routing_algo_ref,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
@ -2208,6 +2229,122 @@ pub async fn default_specific_dynamic_routing_setup(
|
|||||||
Ok(ApplicationResponse::Json(new_record))
|
Ok(ApplicationResponse::Json(new_record))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub async fn create_specific_dynamic_routing_setup(
|
||||||
|
state: &SessionState,
|
||||||
|
key_store: domain::MerchantKeyStore,
|
||||||
|
business_profile: domain::Profile,
|
||||||
|
feature_to_enable: routing_types::DynamicRoutingFeatures,
|
||||||
|
mut dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
|
||||||
|
dynamic_routing_type: routing_types::DynamicRoutingType,
|
||||||
|
payload: routing_types::DynamicRoutingPayload,
|
||||||
|
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
|
||||||
|
let db = state.store.as_ref();
|
||||||
|
let key_manager_state = &state.into();
|
||||||
|
let profile_id = business_profile.get_id().clone();
|
||||||
|
let merchant_id = business_profile.merchant_id.clone();
|
||||||
|
let algorithm_id = common_utils::generate_routing_id_of_default_length();
|
||||||
|
let timestamp = common_utils::date_time::now();
|
||||||
|
|
||||||
|
let algo = match dynamic_routing_type {
|
||||||
|
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
|
||||||
|
let success_config = match &payload {
|
||||||
|
routing_types::DynamicRoutingPayload::SuccessBasedRoutingPayload(config) => config,
|
||||||
|
_ => {
|
||||||
|
return Err((errors::ApiErrorResponse::InvalidRequestData {
|
||||||
|
message: "Invalid payload type for Success Rate Based Routing".to_string(),
|
||||||
|
})
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
routing_algorithm::RoutingAlgorithm {
|
||||||
|
algorithm_id: algorithm_id.clone(),
|
||||||
|
profile_id: profile_id.clone(),
|
||||||
|
merchant_id,
|
||||||
|
name: SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
|
||||||
|
description: None,
|
||||||
|
kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
|
||||||
|
algorithm_data: serde_json::json!(success_config),
|
||||||
|
created_at: timestamp,
|
||||||
|
modified_at: timestamp,
|
||||||
|
algorithm_for: common_enums::TransactionType::Payment,
|
||||||
|
decision_engine_routing_id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
routing_types::DynamicRoutingType::EliminationRouting => {
|
||||||
|
let elimination_config = match &payload {
|
||||||
|
routing_types::DynamicRoutingPayload::EliminationRoutingPayload(config) => config,
|
||||||
|
_ => {
|
||||||
|
return Err((errors::ApiErrorResponse::InvalidRequestData {
|
||||||
|
message: "Invalid payload type for Elimination Routing".to_string(),
|
||||||
|
})
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
routing_algorithm::RoutingAlgorithm {
|
||||||
|
algorithm_id: algorithm_id.clone(),
|
||||||
|
profile_id: profile_id.clone(),
|
||||||
|
merchant_id,
|
||||||
|
name: ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM.to_string(),
|
||||||
|
description: None,
|
||||||
|
kind: diesel_models::enums::RoutingAlgorithmKind::Dynamic,
|
||||||
|
algorithm_data: serde_json::json!(elimination_config),
|
||||||
|
created_at: timestamp,
|
||||||
|
modified_at: timestamp,
|
||||||
|
algorithm_for: common_enums::TransactionType::Payment,
|
||||||
|
decision_engine_routing_id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
routing_types::DynamicRoutingType::ContractBasedRouting => {
|
||||||
|
return Err((errors::ApiErrorResponse::InvalidRequestData {
|
||||||
|
message: "Contract routing cannot be set as default".to_string(),
|
||||||
|
})
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if state.conf.open_router.dynamic_routing_enabled {
|
||||||
|
enable_decision_engine_dynamic_routing_setup(
|
||||||
|
state,
|
||||||
|
business_profile.get_id(),
|
||||||
|
dynamic_routing_type,
|
||||||
|
&mut dynamic_routing_algo_ref,
|
||||||
|
Some(payload),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Unable to setup decision engine dynamic routing")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let record = db
|
||||||
|
.insert_routing_algorithm(algo)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Unable to insert record in routing algorithm table")?;
|
||||||
|
|
||||||
|
dynamic_routing_algo_ref.update_feature(feature_to_enable, dynamic_routing_type);
|
||||||
|
update_business_profile_active_dynamic_algorithm_ref(
|
||||||
|
db,
|
||||||
|
key_manager_state,
|
||||||
|
&key_store,
|
||||||
|
business_profile,
|
||||||
|
dynamic_routing_algo_ref,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let new_record = record.foreign_into();
|
||||||
|
|
||||||
|
core_metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
|
||||||
|
1,
|
||||||
|
router_env::metric_attributes!(("profile_id", profile_id.clone())),
|
||||||
|
);
|
||||||
|
Ok(ApplicationResponse::Json(new_record))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DynamicRoutingConfigParamsInterpolator {
|
pub struct DynamicRoutingConfigParamsInterpolator {
|
||||||
pub payment_method: Option<common_enums::PaymentMethod>,
|
pub payment_method: Option<common_enums::PaymentMethod>,
|
||||||
@ -2289,20 +2426,30 @@ pub async fn enable_decision_engine_dynamic_routing_setup(
|
|||||||
profile_id: &id_type::ProfileId,
|
profile_id: &id_type::ProfileId,
|
||||||
dynamic_routing_type: routing_types::DynamicRoutingType,
|
dynamic_routing_type: routing_types::DynamicRoutingType,
|
||||||
dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
|
dynamic_routing_algo_ref: &mut routing_types::DynamicRoutingAlgorithmRef,
|
||||||
|
payload: Option<routing_types::DynamicRoutingPayload>,
|
||||||
) -> RouterResult<()> {
|
) -> RouterResult<()> {
|
||||||
logger::debug!(
|
logger::debug!(
|
||||||
"performing call with open_router for profile {}",
|
"performing call with open_router for profile {}",
|
||||||
profile_id.get_string_repr()
|
profile_id.get_string_repr()
|
||||||
);
|
);
|
||||||
|
|
||||||
let default_engine_config_request = match dynamic_routing_type {
|
let decision_engine_config_request = match dynamic_routing_type {
|
||||||
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
|
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
|
||||||
let default_success_based_routing_config =
|
let success_based_routing_config = payload
|
||||||
routing_types::SuccessBasedRoutingConfig::open_router_config_default();
|
.and_then(|p| match p {
|
||||||
|
routing_types::DynamicRoutingPayload::SuccessBasedRoutingPayload(config) => {
|
||||||
|
Some(config)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or_else(
|
||||||
|
routing_types::SuccessBasedRoutingConfig::open_router_config_default,
|
||||||
|
);
|
||||||
|
|
||||||
open_router::DecisionEngineConfigSetupRequest {
|
open_router::DecisionEngineConfigSetupRequest {
|
||||||
merchant_id: profile_id.get_string_repr().to_string(),
|
merchant_id: profile_id.get_string_repr().to_string(),
|
||||||
config: open_router::DecisionEngineConfigVariant::SuccessRate(
|
config: open_router::DecisionEngineConfigVariant::SuccessRate(
|
||||||
default_success_based_routing_config
|
success_based_routing_config
|
||||||
.get_decision_engine_configs()
|
.get_decision_engine_configs()
|
||||||
.change_context(errors::ApiErrorResponse::GenericNotFoundError {
|
.change_context(errors::ApiErrorResponse::GenericNotFoundError {
|
||||||
message: "Decision engine config not found".to_string(),
|
message: "Decision engine config not found".to_string(),
|
||||||
@ -2312,12 +2459,21 @@ pub async fn enable_decision_engine_dynamic_routing_setup(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
routing_types::DynamicRoutingType::EliminationRouting => {
|
routing_types::DynamicRoutingType::EliminationRouting => {
|
||||||
let default_elimination_based_routing_config =
|
let elimination_based_routing_config = payload
|
||||||
routing_types::EliminationRoutingConfig::open_router_config_default();
|
.and_then(|p| match p {
|
||||||
|
routing_types::DynamicRoutingPayload::EliminationRoutingPayload(config) => {
|
||||||
|
Some(config)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap_or_else(
|
||||||
|
routing_types::EliminationRoutingConfig::open_router_config_default,
|
||||||
|
);
|
||||||
|
|
||||||
open_router::DecisionEngineConfigSetupRequest {
|
open_router::DecisionEngineConfigSetupRequest {
|
||||||
merchant_id: profile_id.get_string_repr().to_string(),
|
merchant_id: profile_id.get_string_repr().to_string(),
|
||||||
config: open_router::DecisionEngineConfigVariant::Elimination(
|
config: open_router::DecisionEngineConfigVariant::Elimination(
|
||||||
default_elimination_based_routing_config
|
elimination_based_routing_config
|
||||||
.get_decision_engine_configs()
|
.get_decision_engine_configs()
|
||||||
.change_context(errors::ApiErrorResponse::GenericNotFoundError {
|
.change_context(errors::ApiErrorResponse::GenericNotFoundError {
|
||||||
message: "Decision engine config not found".to_string(),
|
message: "Decision engine config not found".to_string(),
|
||||||
@ -2342,7 +2498,7 @@ pub async fn enable_decision_engine_dynamic_routing_setup(
|
|||||||
state,
|
state,
|
||||||
services::Method::Post,
|
services::Method::Post,
|
||||||
DECISION_ENGINE_RULE_CREATE_ENDPOINT,
|
DECISION_ENGINE_RULE_CREATE_ENDPOINT,
|
||||||
Some(default_engine_config_request),
|
Some(decision_engine_config_request),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2193,6 +2193,10 @@ impl Profile {
|
|||||||
web::resource("/toggle")
|
web::resource("/toggle")
|
||||||
.route(web::post().to(routing::toggle_success_based_routing)),
|
.route(web::post().to(routing::toggle_success_based_routing)),
|
||||||
)
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/create")
|
||||||
|
.route(web::post().to(routing::create_success_based_routing)),
|
||||||
|
)
|
||||||
.service(web::resource("/config/{algorithm_id}").route(
|
.service(web::resource("/config/{algorithm_id}").route(
|
||||||
web::patch().to(|state, req, path, payload| {
|
web::patch().to(|state, req, path, payload| {
|
||||||
routing::success_based_routing_update_configs(
|
routing::success_based_routing_update_configs(
|
||||||
@ -2215,6 +2219,10 @@ impl Profile {
|
|||||||
web::resource("/toggle")
|
web::resource("/toggle")
|
||||||
.route(web::post().to(routing::toggle_elimination_routing)),
|
.route(web::post().to(routing::toggle_elimination_routing)),
|
||||||
)
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/create")
|
||||||
|
.route(web::post().to(routing::create_elimination_routing)),
|
||||||
|
)
|
||||||
.service(web::resource("config/{algorithm_id}").route(
|
.service(web::resource("config/{algorithm_id}").route(
|
||||||
web::patch().to(|state, req, path, payload| {
|
web::patch().to(|state, req, path, payload| {
|
||||||
routing::elimination_routing_update_configs(
|
routing::elimination_routing_update_configs(
|
||||||
|
|||||||
@ -79,6 +79,7 @@ impl From<Flow> for ApiIdentifier {
|
|||||||
| Flow::DecisionManagerDeleteConfig
|
| Flow::DecisionManagerDeleteConfig
|
||||||
| Flow::DecisionManagerRetrieveConfig
|
| Flow::DecisionManagerRetrieveConfig
|
||||||
| Flow::ToggleDynamicRouting
|
| Flow::ToggleDynamicRouting
|
||||||
|
| Flow::CreateDynamicRoutingConfig
|
||||||
| Flow::UpdateDynamicRoutingConfigs
|
| Flow::UpdateDynamicRoutingConfigs
|
||||||
| Flow::DecisionManagerUpsertConfig
|
| Flow::DecisionManagerUpsertConfig
|
||||||
| Flow::RoutingEvaluateRule
|
| Flow::RoutingEvaluateRule
|
||||||
|
|||||||
@ -1240,6 +1240,60 @@ pub async fn toggle_success_based_routing(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub async fn create_success_based_routing(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
query: web::Query<api_models::routing::CreateDynamicRoutingQuery>,
|
||||||
|
path: web::Path<routing_types::ToggleDynamicRoutingPath>,
|
||||||
|
success_based_config: web::Json<routing_types::SuccessBasedRoutingConfig>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let flow = Flow::CreateDynamicRoutingConfig;
|
||||||
|
let wrapper = routing_types::CreateDynamicRoutingWrapper {
|
||||||
|
feature_to_enable: query.into_inner().enable,
|
||||||
|
profile_id: path.into_inner().profile_id,
|
||||||
|
payload: api_models::routing::DynamicRoutingPayload::SuccessBasedRoutingPayload(
|
||||||
|
success_based_config.into_inner(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Box::pin(oss_api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state,
|
||||||
|
&req,
|
||||||
|
wrapper.clone(),
|
||||||
|
|state,
|
||||||
|
auth: auth::AuthenticationData,
|
||||||
|
wrapper: routing_types::CreateDynamicRoutingWrapper,
|
||||||
|
_| {
|
||||||
|
let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
|
||||||
|
domain::Context(auth.merchant_account, auth.key_store),
|
||||||
|
));
|
||||||
|
routing::create_specific_dynamic_routing(
|
||||||
|
state,
|
||||||
|
merchant_context,
|
||||||
|
wrapper.feature_to_enable,
|
||||||
|
wrapper.profile_id,
|
||||||
|
api_models::routing::DynamicRoutingType::SuccessRateBasedRouting,
|
||||||
|
wrapper.payload,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
auth::auth_type(
|
||||||
|
&auth::HeaderAuth(auth::ApiKeyAuth {
|
||||||
|
is_connected_allowed: false,
|
||||||
|
is_platform_allowed: false,
|
||||||
|
}),
|
||||||
|
&auth::JWTAuthProfileFromRoute {
|
||||||
|
profile_id: wrapper.profile_id,
|
||||||
|
required_permission: Permission::ProfileRoutingWrite,
|
||||||
|
},
|
||||||
|
req.headers(),
|
||||||
|
),
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
|
#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn success_based_routing_update_configs(
|
pub async fn success_based_routing_update_configs(
|
||||||
@ -1480,6 +1534,60 @@ pub async fn toggle_elimination_routing(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub async fn create_elimination_routing(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
query: web::Query<api_models::routing::CreateDynamicRoutingQuery>,
|
||||||
|
path: web::Path<routing_types::ToggleDynamicRoutingPath>,
|
||||||
|
elimination_config: web::Json<routing_types::EliminationRoutingConfig>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let flow = Flow::CreateDynamicRoutingConfig;
|
||||||
|
let wrapper = routing_types::CreateDynamicRoutingWrapper {
|
||||||
|
feature_to_enable: query.into_inner().enable,
|
||||||
|
profile_id: path.into_inner().profile_id,
|
||||||
|
payload: api_models::routing::DynamicRoutingPayload::EliminationRoutingPayload(
|
||||||
|
elimination_config.into_inner(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Box::pin(oss_api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state,
|
||||||
|
&req,
|
||||||
|
wrapper.clone(),
|
||||||
|
|state,
|
||||||
|
auth: auth::AuthenticationData,
|
||||||
|
wrapper: routing_types::CreateDynamicRoutingWrapper,
|
||||||
|
_| {
|
||||||
|
let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
|
||||||
|
domain::Context(auth.merchant_account, auth.key_store),
|
||||||
|
));
|
||||||
|
routing::create_specific_dynamic_routing(
|
||||||
|
state,
|
||||||
|
merchant_context,
|
||||||
|
wrapper.feature_to_enable,
|
||||||
|
wrapper.profile_id,
|
||||||
|
api_models::routing::DynamicRoutingType::EliminationRouting,
|
||||||
|
wrapper.payload,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
auth::auth_type(
|
||||||
|
&auth::HeaderAuth(auth::ApiKeyAuth {
|
||||||
|
is_connected_allowed: false,
|
||||||
|
is_platform_allowed: false,
|
||||||
|
}),
|
||||||
|
&auth::JWTAuthProfileFromRoute {
|
||||||
|
profile_id: wrapper.profile_id,
|
||||||
|
required_permission: Permission::ProfileRoutingWrite,
|
||||||
|
},
|
||||||
|
req.headers(),
|
||||||
|
),
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn set_dynamic_routing_volume_split(
|
pub async fn set_dynamic_routing_volume_split(
|
||||||
|
|||||||
@ -261,6 +261,8 @@ pub enum Flow {
|
|||||||
RoutingUpdateDefaultConfig,
|
RoutingUpdateDefaultConfig,
|
||||||
/// Routing delete config
|
/// Routing delete config
|
||||||
RoutingDeleteConfig,
|
RoutingDeleteConfig,
|
||||||
|
/// Create dynamic routing
|
||||||
|
CreateDynamicRoutingConfig,
|
||||||
/// Toggle dynamic routing
|
/// Toggle dynamic routing
|
||||||
ToggleDynamicRouting,
|
ToggleDynamicRouting,
|
||||||
/// Update dynamic routing config
|
/// Update dynamic routing config
|
||||||
|
|||||||
Reference in New Issue
Block a user