mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 11:06:50 +08:00
feat(routing): elimination routing switch for toggling the feature (#6568)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -1278,7 +1278,7 @@
|
||||
],
|
||||
"summary": "Routing - Create",
|
||||
"description": "Create a routing algorithm",
|
||||
"operationId": "Create a routing algprithm",
|
||||
"operationId": "Create a routing algorithm",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
|
||||
@ -3929,7 +3929,7 @@
|
||||
],
|
||||
"summary": "Routing - Toggle success based dynamic routing for profile",
|
||||
"description": "Create a success based dynamic routing algorithm",
|
||||
"operationId": "Toggle success based dynamic routing algprithm",
|
||||
"operationId": "Toggle success based dynamic routing algorithm",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "account_id",
|
||||
@ -3955,7 +3955,7 @@
|
||||
"description": "Feature to enable for success based routing",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SuccessBasedRoutingFeatures"
|
||||
"$ref": "#/components/schemas/DynamicRoutingFeatures"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -10234,6 +10234,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DynamicRoutingFeatures": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"metrics",
|
||||
"dynamic_connector_selection",
|
||||
"none"
|
||||
]
|
||||
},
|
||||
"EnabledPaymentMethod": {
|
||||
"type": "object",
|
||||
"description": "Object for EnabledPaymentMethod",
|
||||
@ -23872,14 +23880,6 @@
|
||||
"destination"
|
||||
]
|
||||
},
|
||||
"SuccessBasedRoutingFeatures": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"metrics",
|
||||
"dynamic_connector_selection",
|
||||
"none"
|
||||
]
|
||||
},
|
||||
"SurchargeDetailsResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -24096,6 +24096,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ToggleDynamicRoutingPath": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"profile_id"
|
||||
],
|
||||
"properties": {
|
||||
"profile_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ToggleDynamicRoutingQuery": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"$ref": "#/components/schemas/DynamicRoutingFeatures"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ToggleKVRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -24129,28 +24151,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ToggleSuccessBasedRoutingPath": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"profile_id"
|
||||
],
|
||||
"properties": {
|
||||
"profile_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ToggleSuccessBasedRoutingQuery": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"enable": {
|
||||
"$ref": "#/components/schemas/SuccessBasedRoutingFeatures"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TouchNGoRedirection": {
|
||||
"type": "object"
|
||||
},
|
||||
|
||||
@ -6,7 +6,7 @@ use crate::routing::{
|
||||
RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery,
|
||||
RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, SuccessBasedRoutingConfig,
|
||||
SuccessBasedRoutingPayloadWrapper, SuccessBasedRoutingUpdateConfigQuery,
|
||||
ToggleSuccessBasedRoutingQuery, ToggleSuccessBasedRoutingWrapper,
|
||||
ToggleDynamicRoutingQuery, ToggleDynamicRoutingWrapper,
|
||||
};
|
||||
|
||||
impl ApiEventMetric for RoutingKind {
|
||||
@ -79,7 +79,7 @@ impl ApiEventMetric for RoutingRetrieveLinkQueryWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiEventMetric for ToggleSuccessBasedRoutingQuery {
|
||||
impl ApiEventMetric for ToggleDynamicRoutingQuery {
|
||||
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||
Some(ApiEventsType::Routing)
|
||||
}
|
||||
@ -97,7 +97,7 @@ impl ApiEventMetric for SuccessBasedRoutingPayloadWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiEventMetric for ToggleSuccessBasedRoutingWrapper {
|
||||
impl ApiEventMetric for ToggleDynamicRoutingWrapper {
|
||||
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||
Some(ApiEventsType::Routing)
|
||||
}
|
||||
|
||||
@ -522,6 +522,92 @@ pub struct DynamicAlgorithmWithTimestamp<T> {
|
||||
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DynamicRoutingAlgorithmRef {
|
||||
pub success_based_algorithm: Option<SuccessBasedAlgorithm>,
|
||||
pub elimination_routing_algorithm: Option<EliminationRoutingAlgorithm>,
|
||||
}
|
||||
|
||||
pub trait DynamicRoutingAlgoAccessor {
|
||||
fn get_algorithm_id_with_timestamp(
|
||||
self,
|
||||
) -> DynamicAlgorithmWithTimestamp<common_utils::id_type::RoutingId>;
|
||||
fn get_enabled_features(&mut self) -> &mut DynamicRoutingFeatures;
|
||||
}
|
||||
|
||||
impl DynamicRoutingAlgoAccessor for SuccessBasedAlgorithm {
|
||||
fn get_algorithm_id_with_timestamp(
|
||||
self,
|
||||
) -> DynamicAlgorithmWithTimestamp<common_utils::id_type::RoutingId> {
|
||||
self.algorithm_id_with_timestamp
|
||||
}
|
||||
fn get_enabled_features(&mut self) -> &mut DynamicRoutingFeatures {
|
||||
&mut self.enabled_feature
|
||||
}
|
||||
}
|
||||
|
||||
impl DynamicRoutingAlgoAccessor for EliminationRoutingAlgorithm {
|
||||
fn get_algorithm_id_with_timestamp(
|
||||
self,
|
||||
) -> DynamicAlgorithmWithTimestamp<common_utils::id_type::RoutingId> {
|
||||
self.algorithm_id_with_timestamp
|
||||
}
|
||||
fn get_enabled_features(&mut self) -> &mut DynamicRoutingFeatures {
|
||||
&mut self.enabled_feature
|
||||
}
|
||||
}
|
||||
|
||||
impl EliminationRoutingAlgorithm {
|
||||
pub fn new(
|
||||
algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp<
|
||||
common_utils::id_type::RoutingId,
|
||||
>,
|
||||
) -> Self {
|
||||
Self {
|
||||
algorithm_id_with_timestamp,
|
||||
enabled_feature: DynamicRoutingFeatures::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SuccessBasedAlgorithm {
|
||||
pub fn new(
|
||||
algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp<
|
||||
common_utils::id_type::RoutingId,
|
||||
>,
|
||||
) -> Self {
|
||||
Self {
|
||||
algorithm_id_with_timestamp,
|
||||
enabled_feature: DynamicRoutingFeatures::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DynamicRoutingAlgorithmRef {
|
||||
pub fn update(&mut self, new: Self) {
|
||||
if let Some(elimination_routing_algorithm) = new.elimination_routing_algorithm {
|
||||
self.elimination_routing_algorithm = Some(elimination_routing_algorithm)
|
||||
}
|
||||
if let Some(success_based_algorithm) = new.success_based_algorithm {
|
||||
self.success_based_algorithm = Some(success_based_algorithm)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_specific_ref(
|
||||
&mut self,
|
||||
algo_type: DynamicRoutingType,
|
||||
feature_to_enable: DynamicRoutingFeatures,
|
||||
) {
|
||||
match algo_type {
|
||||
DynamicRoutingType::SuccessRateBasedRouting => {
|
||||
self.success_based_algorithm
|
||||
.as_mut()
|
||||
.map(|algo| algo.enabled_feature = feature_to_enable);
|
||||
}
|
||||
DynamicRoutingType::EliminationRouting => {
|
||||
self.elimination_routing_algorithm
|
||||
.as_mut()
|
||||
.map(|algo| algo.enabled_feature = feature_to_enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
@ -529,11 +615,25 @@ pub struct SuccessBasedAlgorithm {
|
||||
pub algorithm_id_with_timestamp:
|
||||
DynamicAlgorithmWithTimestamp<common_utils::id_type::RoutingId>,
|
||||
#[serde(default)]
|
||||
pub enabled_feature: SuccessBasedRoutingFeatures,
|
||||
pub enabled_feature: DynamicRoutingFeatures,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct EliminationRoutingAlgorithm {
|
||||
pub algorithm_id_with_timestamp:
|
||||
DynamicAlgorithmWithTimestamp<common_utils::id_type::RoutingId>,
|
||||
#[serde(default)]
|
||||
pub enabled_feature: DynamicRoutingFeatures,
|
||||
}
|
||||
|
||||
impl EliminationRoutingAlgorithm {
|
||||
pub fn update_enabled_features(&mut self, feature_to_enable: DynamicRoutingFeatures) {
|
||||
self.enabled_feature = feature_to_enable
|
||||
}
|
||||
}
|
||||
|
||||
impl SuccessBasedAlgorithm {
|
||||
pub fn update_enabled_features(&mut self, feature_to_enable: SuccessBasedRoutingFeatures) {
|
||||
pub fn update_enabled_features(&mut self, feature_to_enable: DynamicRoutingFeatures) {
|
||||
self.enabled_feature = feature_to_enable
|
||||
}
|
||||
}
|
||||
@ -542,26 +642,40 @@ impl DynamicRoutingAlgorithmRef {
|
||||
pub fn update_algorithm_id(
|
||||
&mut self,
|
||||
new_id: common_utils::id_type::RoutingId,
|
||||
enabled_feature: SuccessBasedRoutingFeatures,
|
||||
enabled_feature: DynamicRoutingFeatures,
|
||||
dynamic_routing_type: DynamicRoutingType,
|
||||
) {
|
||||
self.success_based_algorithm = Some(SuccessBasedAlgorithm {
|
||||
algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: Some(new_id),
|
||||
timestamp: common_utils::date_time::now_unix_timestamp(),
|
||||
},
|
||||
enabled_feature,
|
||||
})
|
||||
match dynamic_routing_type {
|
||||
DynamicRoutingType::SuccessRateBasedRouting => {
|
||||
self.success_based_algorithm = Some(SuccessBasedAlgorithm {
|
||||
algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: Some(new_id),
|
||||
timestamp: common_utils::date_time::now_unix_timestamp(),
|
||||
},
|
||||
enabled_feature,
|
||||
})
|
||||
}
|
||||
DynamicRoutingType::EliminationRouting => {
|
||||
self.elimination_routing_algorithm = Some(EliminationRoutingAlgorithm {
|
||||
algorithm_id_with_timestamp: DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: Some(new_id),
|
||||
timestamp: common_utils::date_time::now_unix_timestamp(),
|
||||
},
|
||||
enabled_feature,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
pub struct ToggleSuccessBasedRoutingQuery {
|
||||
pub enable: SuccessBasedRoutingFeatures,
|
||||
pub struct ToggleDynamicRoutingQuery {
|
||||
pub enable: DynamicRoutingFeatures,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SuccessBasedRoutingFeatures {
|
||||
pub enum DynamicRoutingFeatures {
|
||||
Metrics,
|
||||
DynamicConnectorSelection,
|
||||
#[default]
|
||||
@ -577,26 +691,52 @@ pub struct SuccessBasedRoutingUpdateConfigQuery {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct ToggleSuccessBasedRoutingWrapper {
|
||||
pub struct ToggleDynamicRoutingWrapper {
|
||||
pub profile_id: common_utils::id_type::ProfileId,
|
||||
pub feature_to_enable: SuccessBasedRoutingFeatures,
|
||||
pub feature_to_enable: DynamicRoutingFeatures,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||
pub struct ToggleSuccessBasedRoutingPath {
|
||||
pub struct ToggleDynamicRoutingPath {
|
||||
#[schema(value_type = String)]
|
||||
pub profile_id: common_utils::id_type::ProfileId,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
|
||||
pub struct EliminationRoutingConfig {
|
||||
pub params: Option<Vec<DynamicRoutingConfigParams>>,
|
||||
// pub labels: Option<Vec<String>>,
|
||||
pub elimination_analyser_config: Option<EliminationAnalyserConfig>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
|
||||
pub struct EliminationAnalyserConfig {
|
||||
pub bucket_size: Option<u32>,
|
||||
pub bucket_ttl_in_mins: Option<f64>,
|
||||
}
|
||||
|
||||
impl Default for EliminationRoutingConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
params: Some(vec![DynamicRoutingConfigParams::PaymentMethod]),
|
||||
elimination_analyser_config: Some(EliminationAnalyserConfig {
|
||||
bucket_size: Some(5),
|
||||
bucket_ttl_in_mins: Some(2.0),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)]
|
||||
pub struct SuccessBasedRoutingConfig {
|
||||
pub params: Option<Vec<SuccessBasedRoutingConfigParams>>,
|
||||
pub params: Option<Vec<DynamicRoutingConfigParams>>,
|
||||
pub config: Option<SuccessBasedRoutingConfigBody>,
|
||||
}
|
||||
|
||||
impl Default for SuccessBasedRoutingConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
params: Some(vec![SuccessBasedRoutingConfigParams::PaymentMethod]),
|
||||
params: Some(vec![DynamicRoutingConfigParams::PaymentMethod]),
|
||||
config: Some(SuccessBasedRoutingConfigBody {
|
||||
min_aggregates_size: Some(2),
|
||||
default_success_rate: Some(100.0),
|
||||
@ -611,7 +751,7 @@ impl Default for SuccessBasedRoutingConfig {
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema, strum::Display)]
|
||||
pub enum SuccessBasedRoutingConfigParams {
|
||||
pub enum DynamicRoutingConfigParams {
|
||||
PaymentMethod,
|
||||
PaymentMethodType,
|
||||
AuthenticationType,
|
||||
@ -642,6 +782,12 @@ pub struct SuccessBasedRoutingPayloadWrapper {
|
||||
pub profile_id: common_utils::id_type::ProfileId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, strum::Display, serde::Serialize, serde::Deserialize)]
|
||||
pub enum DynamicRoutingType {
|
||||
SuccessRateBasedRouting,
|
||||
EliminationRouting,
|
||||
}
|
||||
|
||||
impl SuccessBasedRoutingConfig {
|
||||
pub fn update(&mut self, new: Self) {
|
||||
if let Some(params) = new.params {
|
||||
|
||||
@ -575,7 +575,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::routing::RoutingDictionaryRecord,
|
||||
api_models::routing::RoutingKind,
|
||||
api_models::routing::RoutableConnectorChoice,
|
||||
api_models::routing::SuccessBasedRoutingFeatures,
|
||||
api_models::routing::DynamicRoutingFeatures,
|
||||
api_models::routing::LinkedRoutingConfigRetrieveResponse,
|
||||
api_models::routing::RoutingRetrieveResponse,
|
||||
api_models::routing::ProfileDefaultRoutingConfig,
|
||||
@ -586,8 +586,8 @@ Never share your secret api keys. Keep them guarded and secure.
|
||||
api_models::routing::StraightThroughAlgorithm,
|
||||
api_models::routing::ConnectorVolumeSplit,
|
||||
api_models::routing::ConnectorSelection,
|
||||
api_models::routing::ToggleSuccessBasedRoutingQuery,
|
||||
api_models::routing::ToggleSuccessBasedRoutingPath,
|
||||
api_models::routing::ToggleDynamicRoutingQuery,
|
||||
api_models::routing::ToggleDynamicRoutingPath,
|
||||
api_models::routing::ast::RoutableChoiceKind,
|
||||
api_models::enums::RoutableConnectors,
|
||||
api_models::routing::ast::ProgramConnectorSelection,
|
||||
|
||||
@ -37,7 +37,7 @@ pub async fn routing_create_config() {}
|
||||
(status = 403, description = "Forbidden"),
|
||||
),
|
||||
tag = "Routing",
|
||||
operation_id = "Create a routing algprithm",
|
||||
operation_id = "Create a routing algorithm",
|
||||
security(("api_key" = []), ("jwt_key" = []))
|
||||
)]
|
||||
pub async fn routing_create_config() {}
|
||||
@ -264,7 +264,7 @@ pub async fn routing_update_default_config_for_profile() {}
|
||||
params(
|
||||
("account_id" = String, Path, description = "Merchant id"),
|
||||
("profile_id" = String, Path, description = "Profile id under which Dynamic routing needs to be toggled"),
|
||||
("enable" = SuccessBasedRoutingFeatures, Query, description = "Feature to enable for success based routing"),
|
||||
("enable" = DynamicRoutingFeatures, Query, description = "Feature to enable for success based routing"),
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Routing Algorithm created", body = RoutingDictionaryRecord),
|
||||
@ -275,7 +275,33 @@ pub async fn routing_update_default_config_for_profile() {}
|
||||
(status = 403, description = "Forbidden"),
|
||||
),
|
||||
tag = "Routing",
|
||||
operation_id = "Toggle success based dynamic routing algprithm",
|
||||
operation_id = "Toggle success based dynamic routing algorithm",
|
||||
security(("api_key" = []), ("jwt_key" = []))
|
||||
)]
|
||||
pub async fn toggle_success_based_routing() {}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
/// Routing - Toggle elimination routing for profile
|
||||
///
|
||||
/// Create a elimination based dynamic routing algorithm
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/account/:account_id/business_profile/:profile_id/dynamic_routing/elimination/toggle",
|
||||
params(
|
||||
("account_id" = String, Path, description = "Merchant id"),
|
||||
("profile_id" = String, Path, description = "Profile id under which Dynamic routing needs to be toggled"),
|
||||
("enable" = DynamicRoutingFeatures, Query, description = "Feature to enable for success based routing"),
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Routing Algorithm created", body = RoutingDictionaryRecord),
|
||||
(status = 400, description = "Request body is malformed"),
|
||||
(status = 500, description = "Internal server error"),
|
||||
(status = 404, description = "Resource missing"),
|
||||
(status = 422, description = "Unprocessable request"),
|
||||
(status = 403, description = "Forbidden"),
|
||||
),
|
||||
tag = "Routing",
|
||||
operation_id = "Toggle elimination routing algorithm",
|
||||
security(("api_key" = []), ("jwt_key" = []))
|
||||
)]
|
||||
pub async fn toggle_elimination_routing() {}
|
||||
|
||||
@ -1263,7 +1263,7 @@ pub async fn perform_success_based_routing(
|
||||
)?;
|
||||
|
||||
if success_based_algo_ref.enabled_feature
|
||||
== api_routing::SuccessBasedRoutingFeatures::DynamicConnectorSelection
|
||||
== api_routing::DynamicRoutingFeatures::DynamicConnectorSelection
|
||||
{
|
||||
logger::debug!(
|
||||
"performing success_based_routing for profile {}",
|
||||
|
||||
@ -452,6 +452,16 @@ pub async fn link_routing_config(
|
||||
},
|
||||
enabled_feature: _
|
||||
}) if id == &algorithm_id
|
||||
) || matches!(
|
||||
dynamic_routing_ref.elimination_routing_algorithm,
|
||||
Some(routing::EliminationRoutingAlgorithm {
|
||||
algorithm_id_with_timestamp:
|
||||
routing_types::DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: Some(ref id),
|
||||
timestamp: _
|
||||
},
|
||||
enabled_feature: _
|
||||
}) if id == &algorithm_id
|
||||
),
|
||||
|| {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
@ -470,7 +480,9 @@ pub async fn link_routing_config(
|
||||
"missing success_based_algorithm in dynamic_algorithm_ref from business_profile table",
|
||||
)?
|
||||
.enabled_feature,
|
||||
routing_types::DynamicRoutingType::SuccessRateBasedRouting,
|
||||
);
|
||||
|
||||
helpers::update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
key_manager_state,
|
||||
@ -1185,13 +1197,16 @@ pub async fn update_default_routing_config_for_profile(
|
||||
))
|
||||
}
|
||||
|
||||
// Toggle the specific routing type as well as add the default configs in RoutingAlgorithm table
|
||||
// and update the same in business profile table.
|
||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||
pub async fn toggle_success_based_routing(
|
||||
pub async fn toggle_specific_dynamic_routing(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
feature_to_enable: routing::SuccessBasedRoutingFeatures,
|
||||
feature_to_enable: routing::DynamicRoutingFeatures,
|
||||
profile_id: common_utils::id_type::ProfileId,
|
||||
dynamic_routing_type: routing::DynamicRoutingType,
|
||||
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||
metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(
|
||||
&metrics::CONTEXT,
|
||||
@ -1214,170 +1229,44 @@ pub async fn toggle_success_based_routing(
|
||||
id: profile_id.get_string_repr().to_owned(),
|
||||
})?;
|
||||
|
||||
let mut success_based_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();
|
||||
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::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?;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
} 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::DynamicRoutingFeatures::Metrics
|
||||
| routing::DynamicRoutingFeatures::DynamicConnectorSelection => {
|
||||
// occurs when algorithm is already present in the db
|
||||
// 1. If present with same feature then return response as already enabled
|
||||
// 2. Else update the feature and persist the same on db
|
||||
// 3. If not present in db then create a new default entry
|
||||
helpers::enable_dynamic_routing_algorithm(
|
||||
&state,
|
||||
key_store,
|
||||
business_profile,
|
||||
feature_to_enable,
|
||||
dynamic_routing_algo_ref,
|
||||
dynamic_routing_type,
|
||||
)
|
||||
.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(),
|
||||
})?,
|
||||
}
|
||||
routing::DynamicRoutingFeatures::None => {
|
||||
// disable specific dynamic routing for the requested profile
|
||||
helpers::disable_dynamic_routing_algorithm(
|
||||
&state,
|
||||
key_store,
|
||||
business_profile,
|
||||
dynamic_routing_algo_ref,
|
||||
dynamic_routing_type,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,8 @@ use router_env::{instrument, metrics::add_attributes, tracing};
|
||||
use rustc_hash::FxHashSet;
|
||||
use storage_impl::redis::cache;
|
||||
|
||||
#[cfg(all(feature = "dynamic_routing", feature = "v1"))]
|
||||
use crate::db::errors::StorageErrorExt;
|
||||
#[cfg(feature = "v2")]
|
||||
use crate::types::domain::MerchantConnectorAccount;
|
||||
use crate::{
|
||||
@ -39,6 +41,8 @@ use crate::{
|
||||
use crate::{core::metrics as core_metrics, routes::metrics, types::transformers::ForeignInto};
|
||||
pub const SUCCESS_BASED_DYNAMIC_ROUTING_ALGORITHM: &str =
|
||||
"Success rate based dynamic routing algorithm";
|
||||
pub const ELIMINATION_BASED_DYNAMIC_ROUTING_ALGORITHM: &str =
|
||||
"Elimination based dynamic routing algorithm";
|
||||
|
||||
/// Provides us with all the configured configs of the Merchant in the ascending time configured
|
||||
/// manner and chooses the first of them
|
||||
@ -263,9 +267,9 @@ pub async fn update_business_profile_active_dynamic_algorithm_ref(
|
||||
key_manager_state: &KeyManagerState,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
current_business_profile: domain::Profile,
|
||||
dynamic_routing_algorithm: routing_types::DynamicRoutingAlgorithmRef,
|
||||
dynamic_routing_algorithm_ref: routing_types::DynamicRoutingAlgorithmRef,
|
||||
) -> RouterResult<()> {
|
||||
let ref_val = dynamic_routing_algorithm
|
||||
let ref_val = dynamic_routing_algorithm_ref
|
||||
.encode_to_value()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to convert dynamic routing ref to value")?;
|
||||
@ -662,7 +666,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing(
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("success_based_algorithm not found in dynamic_routing_algorithm from business_profile table")?;
|
||||
|
||||
if success_based_algo_ref.enabled_feature != routing_types::SuccessBasedRoutingFeatures::None {
|
||||
if success_based_algo_ref.enabled_feature != routing_types::DynamicRoutingFeatures::None {
|
||||
let client = state
|
||||
.grpc_client
|
||||
.dynamic_routing
|
||||
@ -912,34 +916,303 @@ pub fn generate_tenant_business_profile_id(
|
||||
format!("{}:{}", redis_key_prefix, business_profile_id)
|
||||
}
|
||||
|
||||
/// default config setup for success_based_routing
|
||||
#[cfg(feature = "v1")]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn default_success_based_routing_setup(
|
||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||
pub async fn disable_dynamic_routing_algorithm(
|
||||
state: &SessionState,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
business_profile: domain::Profile,
|
||||
feature_to_enable: routing_types::SuccessBasedRoutingFeatures,
|
||||
merchant_id: id_type::MerchantId,
|
||||
mut success_based_dynamic_routing_algo: routing_types::DynamicRoutingAlgorithmRef,
|
||||
dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
|
||||
dynamic_routing_type: routing_types::DynamicRoutingType,
|
||||
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
|
||||
let db = state.store.as_ref();
|
||||
let key_manager_state = &state.into();
|
||||
let profile_id = business_profile.get_id().to_owned();
|
||||
let default_success_based_routing_config = routing_types::SuccessBasedRoutingConfig::default();
|
||||
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||
let profile_id = business_profile
|
||||
.get_id()
|
||||
.clone()
|
||||
.get_string_repr()
|
||||
.to_owned();
|
||||
let (algorithm_id, dynamic_routing_algorithm, cache_entries_to_redact) =
|
||||
match dynamic_routing_type {
|
||||
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
|
||||
let Some(algorithm_ref) = dynamic_routing_algo_ref.success_based_algorithm else {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Success rate based routing is already disabled".to_string(),
|
||||
})?
|
||||
};
|
||||
let Some(algorithm_id) = algorithm_ref.algorithm_id_with_timestamp.algorithm_id
|
||||
else {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})?
|
||||
};
|
||||
|
||||
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(),
|
||||
)];
|
||||
(
|
||||
algorithm_id,
|
||||
routing_types::DynamicRoutingAlgorithmRef {
|
||||
success_based_algorithm: Some(routing_types::SuccessBasedAlgorithm {
|
||||
algorithm_id_with_timestamp:
|
||||
routing_types::DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: None,
|
||||
timestamp,
|
||||
},
|
||||
enabled_feature: routing_types::DynamicRoutingFeatures::None,
|
||||
}),
|
||||
elimination_routing_algorithm: dynamic_routing_algo_ref
|
||||
.elimination_routing_algorithm,
|
||||
},
|
||||
cache_entries_to_redact,
|
||||
)
|
||||
}
|
||||
routing_types::DynamicRoutingType::EliminationRouting => {
|
||||
let Some(algorithm_ref) = dynamic_routing_algo_ref.elimination_routing_algorithm
|
||||
else {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Elimination routing is already disabled".to_string(),
|
||||
})?
|
||||
};
|
||||
let Some(algorithm_id) = algorithm_ref.algorithm_id_with_timestamp.algorithm_id
|
||||
else {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "Algorithm is already inactive".to_string(),
|
||||
})?
|
||||
};
|
||||
let cache_key = format!(
|
||||
"{}_{}",
|
||||
business_profile.get_id().get_string_repr(),
|
||||
algorithm_id.get_string_repr()
|
||||
);
|
||||
let cache_entries_to_redact =
|
||||
vec![cache::CacheKind::EliminationBasedDynamicRoutingCache(
|
||||
cache_key.into(),
|
||||
)];
|
||||
(
|
||||
algorithm_id,
|
||||
routing_types::DynamicRoutingAlgorithmRef {
|
||||
success_based_algorithm: dynamic_routing_algo_ref.success_based_algorithm,
|
||||
elimination_routing_algorithm: Some(
|
||||
routing_types::EliminationRoutingAlgorithm {
|
||||
algorithm_id_with_timestamp:
|
||||
routing_types::DynamicAlgorithmWithTimestamp {
|
||||
algorithm_id: None,
|
||||
timestamp,
|
||||
},
|
||||
enabled_feature: routing_types::DynamicRoutingFeatures::None,
|
||||
},
|
||||
),
|
||||
},
|
||||
cache_entries_to_redact,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// redact cache for dynamic routing config
|
||||
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 dynamic 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();
|
||||
update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
business_profile,
|
||||
dynamic_routing_algorithm,
|
||||
)
|
||||
.await?;
|
||||
|
||||
core_metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([("profile_id", profile_id)]),
|
||||
);
|
||||
|
||||
Ok(ApplicationResponse::Json(response))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||
pub async fn enable_dynamic_routing_algorithm(
|
||||
state: &SessionState,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
business_profile: domain::Profile,
|
||||
feature_to_enable: routing_types::DynamicRoutingFeatures,
|
||||
dynamic_routing_algo_ref: routing_types::DynamicRoutingAlgorithmRef,
|
||||
dynamic_routing_type: routing_types::DynamicRoutingType,
|
||||
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>> {
|
||||
let dynamic_routing = dynamic_routing_algo_ref.clone();
|
||||
match dynamic_routing_type {
|
||||
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
|
||||
enable_specific_routing_algorithm(
|
||||
state,
|
||||
key_store,
|
||||
business_profile,
|
||||
feature_to_enable,
|
||||
dynamic_routing_algo_ref,
|
||||
dynamic_routing_type,
|
||||
dynamic_routing.success_based_algorithm,
|
||||
)
|
||||
.await
|
||||
}
|
||||
routing_types::DynamicRoutingType::EliminationRouting => {
|
||||
enable_specific_routing_algorithm(
|
||||
state,
|
||||
key_store,
|
||||
business_profile,
|
||||
feature_to_enable,
|
||||
dynamic_routing_algo_ref,
|
||||
dynamic_routing_type,
|
||||
dynamic_routing.elimination_routing_algorithm,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "v1", feature = "dynamic_routing"))]
|
||||
pub async fn enable_specific_routing_algorithm<A>(
|
||||
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,
|
||||
algo_type: Option<A>,
|
||||
) -> RouterResult<ApplicationResponse<routing_types::RoutingDictionaryRecord>>
|
||||
where
|
||||
A: routing_types::DynamicRoutingAlgoAccessor + Clone + std::fmt::Debug,
|
||||
{
|
||||
// Algorithm wasn't created yet
|
||||
let Some(mut algo_type) = algo_type else {
|
||||
return default_specific_dynamic_routing_setup(
|
||||
state,
|
||||
key_store,
|
||||
business_profile,
|
||||
feature_to_enable,
|
||||
dynamic_routing_algo_ref,
|
||||
dynamic_routing_type,
|
||||
)
|
||||
.await;
|
||||
};
|
||||
|
||||
// Algorithm was in disabled state
|
||||
let Some(algo_type_algorithm_id) = algo_type
|
||||
.clone()
|
||||
.get_algorithm_id_with_timestamp()
|
||||
.algorithm_id
|
||||
else {
|
||||
return default_specific_dynamic_routing_setup(
|
||||
state,
|
||||
key_store,
|
||||
business_profile,
|
||||
feature_to_enable,
|
||||
dynamic_routing_algo_ref,
|
||||
dynamic_routing_type,
|
||||
)
|
||||
.await;
|
||||
};
|
||||
let db = state.store.as_ref();
|
||||
let profile_id = business_profile.get_id().clone();
|
||||
let algo_type_enabled_features = algo_type.get_enabled_features();
|
||||
if *algo_type_enabled_features == feature_to_enable {
|
||||
// algorithm already has the required feature
|
||||
return Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: format!("{} is already enabled", dynamic_routing_type),
|
||||
}
|
||||
.into());
|
||||
};
|
||||
*algo_type_enabled_features = feature_to_enable.clone();
|
||||
dynamic_routing_algo_ref
|
||||
.update_specific_ref(dynamic_routing_type.clone(), feature_to_enable.clone());
|
||||
update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
&state.into(),
|
||||
&key_store,
|
||||
business_profile,
|
||||
dynamic_routing_algo_ref.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let routing_algorithm = db
|
||||
.find_routing_algorithm_by_profile_id_algorithm_id(&profile_id, &algo_type_algorithm_id)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||
let updated_routing_record = routing_algorithm.foreign_into();
|
||||
core_metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(
|
||||
&metrics::CONTEXT,
|
||||
1,
|
||||
&add_attributes([("profile_id", profile_id.get_string_repr().to_owned())]),
|
||||
);
|
||||
Ok(ApplicationResponse::Json(updated_routing_record))
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn default_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,
|
||||
) -> 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 = 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!(default_success_based_routing_config),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: common_enums::TransactionType::Payment,
|
||||
let algo = match dynamic_routing_type {
|
||||
routing_types::DynamicRoutingType::SuccessRateBasedRouting => {
|
||||
let default_success_based_routing_config =
|
||||
routing_types::SuccessBasedRoutingConfig::default();
|
||||
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!(default_success_based_routing_config),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: common_enums::TransactionType::Payment,
|
||||
}
|
||||
}
|
||||
routing_types::DynamicRoutingType::EliminationRouting => {
|
||||
let default_elimination_routing_config =
|
||||
routing_types::EliminationRoutingConfig::default();
|
||||
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!(default_elimination_routing_config),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
algorithm_for: common_enums::TransactionType::Payment,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let record = db
|
||||
@ -948,13 +1221,17 @@ pub async fn default_success_based_routing_setup(
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Unable to insert record in routing algorithm table")?;
|
||||
|
||||
success_based_dynamic_routing_algo.update_algorithm_id(algorithm_id, feature_to_enable);
|
||||
dynamic_routing_algo_ref.update_algorithm_id(
|
||||
algorithm_id,
|
||||
feature_to_enable,
|
||||
dynamic_routing_type,
|
||||
);
|
||||
update_business_profile_active_dynamic_algorithm_ref(
|
||||
db,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
business_profile,
|
||||
success_based_dynamic_routing_algo,
|
||||
dynamic_routing_algo_ref,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -1001,35 +1278,35 @@ impl SuccessBasedRoutingConfigParamsInterpolator {
|
||||
|
||||
pub fn get_string_val(
|
||||
&self,
|
||||
params: &Vec<routing_types::SuccessBasedRoutingConfigParams>,
|
||||
params: &Vec<routing_types::DynamicRoutingConfigParams>,
|
||||
) -> String {
|
||||
let mut parts: Vec<String> = Vec::new();
|
||||
for param in params {
|
||||
let val = match param {
|
||||
routing_types::SuccessBasedRoutingConfigParams::PaymentMethod => self
|
||||
routing_types::DynamicRoutingConfigParams::PaymentMethod => self
|
||||
.payment_method
|
||||
.as_ref()
|
||||
.map_or(String::new(), |pm| pm.to_string()),
|
||||
routing_types::SuccessBasedRoutingConfigParams::PaymentMethodType => self
|
||||
routing_types::DynamicRoutingConfigParams::PaymentMethodType => self
|
||||
.payment_method_type
|
||||
.as_ref()
|
||||
.map_or(String::new(), |pmt| pmt.to_string()),
|
||||
routing_types::SuccessBasedRoutingConfigParams::AuthenticationType => self
|
||||
routing_types::DynamicRoutingConfigParams::AuthenticationType => self
|
||||
.authentication_type
|
||||
.as_ref()
|
||||
.map_or(String::new(), |at| at.to_string()),
|
||||
routing_types::SuccessBasedRoutingConfigParams::Currency => self
|
||||
routing_types::DynamicRoutingConfigParams::Currency => self
|
||||
.currency
|
||||
.as_ref()
|
||||
.map_or(String::new(), |cur| cur.to_string()),
|
||||
routing_types::SuccessBasedRoutingConfigParams::Country => self
|
||||
routing_types::DynamicRoutingConfigParams::Country => self
|
||||
.country
|
||||
.as_ref()
|
||||
.map_or(String::new(), |cn| cn.to_string()),
|
||||
routing_types::SuccessBasedRoutingConfigParams::CardNetwork => {
|
||||
routing_types::DynamicRoutingConfigParams::CardNetwork => {
|
||||
self.card_network.clone().unwrap_or_default()
|
||||
}
|
||||
routing_types::SuccessBasedRoutingConfigParams::CardBin => {
|
||||
routing_types::DynamicRoutingConfigParams::CardBin => {
|
||||
self.card_bin.clone().unwrap_or_default()
|
||||
}
|
||||
};
|
||||
|
||||
@ -1755,9 +1755,9 @@ impl Profile {
|
||||
|
||||
#[cfg(feature = "dynamic_routing")]
|
||||
{
|
||||
route =
|
||||
route.service(
|
||||
web::scope("/{profile_id}/dynamic_routing").service(
|
||||
route = route.service(
|
||||
web::scope("/{profile_id}/dynamic_routing")
|
||||
.service(
|
||||
web::scope("/success_based")
|
||||
.service(
|
||||
web::resource("/toggle")
|
||||
@ -1770,8 +1770,14 @@ impl Profile {
|
||||
)
|
||||
}),
|
||||
)),
|
||||
)
|
||||
.service(
|
||||
web::scope("/elimination").service(
|
||||
web::resource("/toggle")
|
||||
.route(web::post().to(routing::toggle_elimination_routing)),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
route = route.service(
|
||||
|
||||
@ -1014,11 +1014,11 @@ pub async fn routing_update_default_config_for_profile(
|
||||
pub async fn toggle_success_based_routing(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
query: web::Query<api_models::routing::ToggleSuccessBasedRoutingQuery>,
|
||||
path: web::Path<routing_types::ToggleSuccessBasedRoutingPath>,
|
||||
query: web::Query<api_models::routing::ToggleDynamicRoutingQuery>,
|
||||
path: web::Path<routing_types::ToggleDynamicRoutingPath>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ToggleDynamicRouting;
|
||||
let wrapper = routing_types::ToggleSuccessBasedRoutingWrapper {
|
||||
let wrapper = routing_types::ToggleDynamicRoutingWrapper {
|
||||
feature_to_enable: query.into_inner().enable,
|
||||
profile_id: path.into_inner().profile_id,
|
||||
};
|
||||
@ -1029,14 +1029,15 @@ pub async fn toggle_success_based_routing(
|
||||
wrapper.clone(),
|
||||
|state,
|
||||
auth: auth::AuthenticationData,
|
||||
wrapper: routing_types::ToggleSuccessBasedRoutingWrapper,
|
||||
wrapper: routing_types::ToggleDynamicRoutingWrapper,
|
||||
_| {
|
||||
routing::toggle_success_based_routing(
|
||||
routing::toggle_specific_dynamic_routing(
|
||||
state,
|
||||
auth.merchant_account,
|
||||
auth.key_store,
|
||||
wrapper.feature_to_enable,
|
||||
wrapper.profile_id,
|
||||
api_models::routing::DynamicRoutingType::SuccessRateBasedRouting,
|
||||
)
|
||||
},
|
||||
auth::auth_type(
|
||||
@ -1085,3 +1086,46 @@ pub async fn success_based_routing_update_configs(
|
||||
))
|
||||
.await
|
||||
}
|
||||
#[cfg(all(feature = "olap", feature = "v1", feature = "dynamic_routing"))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn toggle_elimination_routing(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
query: web::Query<api_models::routing::ToggleDynamicRoutingQuery>,
|
||||
path: web::Path<routing_types::ToggleDynamicRoutingPath>,
|
||||
) -> impl Responder {
|
||||
let flow = Flow::ToggleDynamicRouting;
|
||||
let wrapper = routing_types::ToggleDynamicRoutingWrapper {
|
||||
feature_to_enable: query.into_inner().enable,
|
||||
profile_id: path.into_inner().profile_id,
|
||||
};
|
||||
Box::pin(oss_api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
wrapper.clone(),
|
||||
|state,
|
||||
auth: auth::AuthenticationData,
|
||||
wrapper: routing_types::ToggleDynamicRoutingWrapper,
|
||||
_| {
|
||||
routing::toggle_specific_dynamic_routing(
|
||||
state,
|
||||
auth.merchant_account,
|
||||
auth.key_store,
|
||||
wrapper.feature_to_enable,
|
||||
wrapper.profile_id,
|
||||
api_models::routing::DynamicRoutingType::EliminationRouting,
|
||||
)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth),
|
||||
&auth::JWTAuthProfileFromRoute {
|
||||
profile_id: wrapper.profile_id,
|
||||
required_permission: Permission::ProfileRoutingWrite,
|
||||
},
|
||||
req.headers(),
|
||||
),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ pub static PM_FILTERS_CGRAPH_CACHE: Lazy<Cache> = Lazy::new(|| {
|
||||
)
|
||||
});
|
||||
|
||||
/// Dynamic Algorithm Cache
|
||||
/// Success based Dynamic Algorithm Cache
|
||||
pub static SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE: Lazy<Cache> = Lazy::new(|| {
|
||||
Cache::new(
|
||||
"SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE",
|
||||
@ -82,6 +82,16 @@ pub static SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE: Lazy<Cache> = Lazy::new(|| {
|
||||
)
|
||||
});
|
||||
|
||||
/// Elimination based Dynamic Algorithm Cache
|
||||
pub static ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE: Lazy<Cache> = Lazy::new(|| {
|
||||
Cache::new(
|
||||
"ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE",
|
||||
CACHE_TTL,
|
||||
CACHE_TTI,
|
||||
Some(MAX_CAPACITY),
|
||||
)
|
||||
});
|
||||
|
||||
/// Trait which defines the behaviour of types that's gonna be stored in Cache
|
||||
pub trait Cacheable: Any + Send + Sync + DynClone {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
@ -102,6 +112,7 @@ pub enum CacheKind<'a> {
|
||||
Surcharge(Cow<'a, str>),
|
||||
CGraph(Cow<'a, str>),
|
||||
SuccessBasedDynamicRoutingCache(Cow<'a, str>),
|
||||
EliminationBasedDynamicRoutingCache(Cow<'a, str>),
|
||||
PmFiltersCGraph(Cow<'a, str>),
|
||||
All(Cow<'a, str>),
|
||||
}
|
||||
|
||||
@ -6,8 +6,8 @@ use router_env::{logger, tracing::Instrument};
|
||||
|
||||
use crate::redis::cache::{
|
||||
CacheKey, CacheKind, CacheRedact, ACCOUNTS_CACHE, CGRAPH_CACHE, CONFIG_CACHE,
|
||||
DECISION_MANAGER_CACHE, PM_FILTERS_CGRAPH_CACHE, ROUTING_CACHE,
|
||||
SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE, SURCHARGE_CACHE,
|
||||
DECISION_MANAGER_CACHE, ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE, PM_FILTERS_CGRAPH_CACHE,
|
||||
ROUTING_CACHE, SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE, SURCHARGE_CACHE,
|
||||
};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -138,6 +138,15 @@ impl PubSubInterface for std::sync::Arc<redis_interface::RedisConnectionPool> {
|
||||
.await;
|
||||
key
|
||||
}
|
||||
CacheKind::EliminationBasedDynamicRoutingCache(key) => {
|
||||
ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE
|
||||
.remove(CacheKey {
|
||||
key: key.to_string(),
|
||||
prefix: message.tenant.clone(),
|
||||
})
|
||||
.await;
|
||||
key
|
||||
}
|
||||
CacheKind::SuccessBasedDynamicRoutingCache(key) => {
|
||||
SUCCESS_BASED_DYNAMIC_ALGORITHM_CACHE
|
||||
.remove(CacheKey {
|
||||
@ -205,6 +214,12 @@ impl PubSubInterface for std::sync::Arc<redis_interface::RedisConnectionPool> {
|
||||
prefix: message.tenant.clone(),
|
||||
})
|
||||
.await;
|
||||
ELIMINATION_BASED_DYNAMIC_ALGORITHM_CACHE
|
||||
.remove(CacheKey {
|
||||
key: key.to_string(),
|
||||
prefix: message.tenant.clone(),
|
||||
})
|
||||
.await;
|
||||
ROUTING_CACHE
|
||||
.remove(CacheKey {
|
||||
key: key.to_string(),
|
||||
|
||||
Reference in New Issue
Block a user