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:
AnkitKmrGupta
2025-08-19 13:08:36 +05:30
committed by GitHub
parent 446590690e
commit 58abb604d7
8 changed files with 410 additions and 10 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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(

View File

@ -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,
) )

View File

@ -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(

View File

@ -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

View File

@ -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(

View File

@ -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