From 9570b5a3e073a0b2dae88fb6b3cb93846f89d323 Mon Sep 17 00:00:00 2001 From: Chethan Date: Thu, 22 May 2025 22:24:00 +0530 Subject: [PATCH 1/4] feat(dynamic_routing): add get api for dynamic routing volume split --- crates/api_models/src/events/routing.rs | 18 +++++++++-- crates/api_models/src/routing.rs | 5 +++ crates/openapi/src/openapi.rs | 1 + crates/router/src/core/routing.rs | 42 +++++++++++++++++++++++++ crates/router/src/routes/app.rs | 4 +++ crates/router/src/routes/routing.rs | 41 ++++++++++++++++++++++++ 6 files changed, 108 insertions(+), 3 deletions(-) diff --git a/crates/api_models/src/events/routing.rs b/crates/api_models/src/events/routing.rs index c9fa118f596..62b3b2713cf 100644 --- a/crates/api_models/src/events/routing.rs +++ b/crates/api_models/src/events/routing.rs @@ -6,9 +6,9 @@ use crate::routing::{ LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig, RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind, RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery, - RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplitWrapper, - SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, ToggleDynamicRoutingQuery, - ToggleDynamicRoutingWrapper, + RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplitResponse, + RoutingVolumeSplitWrapper, SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, + ToggleDynamicRoutingPath, ToggleDynamicRoutingQuery, ToggleDynamicRoutingWrapper, }; impl ApiEventMetric for RoutingKind { @@ -134,3 +134,15 @@ impl ApiEventMetric for RoutingVolumeSplitWrapper { Some(ApiEventsType::Routing) } } + +impl ApiEventMetric for ToggleDynamicRoutingPath { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for RoutingVolumeSplitResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index e1802ab56f5..d6093325e0d 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -830,6 +830,11 @@ pub struct ToggleDynamicRoutingPath { pub profile_id: common_utils::id_type::ProfileId, } +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] +pub struct RoutingVolumeSplitResponse { + pub split: u8, +} + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, ToSchema)] pub struct EliminationRoutingConfig { pub params: Option>, diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 79c4467f039..101c2a914c8 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -665,6 +665,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::routing::SuccessRateSpecificityLevel, api_models::routing::ToggleDynamicRoutingQuery, api_models::routing::ToggleDynamicRoutingPath, + api_models::routing::RoutingVolumeSplitResponse, api_models::routing::ast::RoutableChoiceKind, api_models::enums::RoutableConnectors, api_models::routing::ast::ProgramConnectorSelection, diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 6b86bb83f54..625b168cbc4 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -1475,6 +1475,48 @@ pub async fn configure_dynamic_routing_volume_split( Ok(service_api::ApplicationResponse::StatusOk) } +#[cfg(feature = "v1")] +pub async fn retrieve_dynamic_routing_volume_split( + state: SessionState, + merchant_context: domain::MerchantContext, + profile_id: common_utils::id_type::ProfileId, +) -> RouterResponse { + 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(); + + let resp = routing_types::RoutingVolumeSplitResponse { + split: dynamic_routing_algo_ref + .dynamic_routing_volume_split + .unwrap_or_default(), + }; + + Ok(service_api::ApplicationResponse::Json(resp)) +} + #[cfg(all(feature = "v1", feature = "dynamic_routing"))] pub async fn success_based_routing_update_configs( state: SessionState, diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index bc5d58e4825..7d3d1f267f9 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -2052,6 +2052,10 @@ impl Profile { web::resource("/set_volume_split") .route(web::post().to(routing::set_dynamic_routing_volume_split)), ) + .service( + web::resource("/get_volume_split") + .route(web::get().to(routing::get_dynamic_routing_volume_split)), + ) .service( web::scope("/elimination") .service( diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index ba041cbe5ac..ff8ad06e91c 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -1502,3 +1502,44 @@ pub async fn set_dynamic_routing_volume_split( )) .await } + +#[cfg(all(feature = "olap", feature = "v1"))] +#[instrument(skip_all)] +pub async fn get_dynamic_routing_volume_split( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> impl Responder { + let flow = Flow::VolumeSplitOnRoutingType; + + let payload = path.into_inner(); + Box::pin(oss_api::server_wrap( + flow, + state, + &req, + payload.clone(), + |state, auth: auth::AuthenticationData, payload, _| { + let merchant_context = domain::MerchantContext::NormalMerchant(Box::new( + domain::Context(auth.merchant_account, auth.key_store), + )); + routing::retrieve_dynamic_routing_volume_split( + state, + merchant_context, + payload.profile_id, + ) + }, + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth { + is_connected_allowed: false, + is_platform_allowed: false, + }), + &auth::JWTAuthProfileFromRoute { + profile_id: payload.profile_id, + required_permission: Permission::ProfileRoutingRead, + }, + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} From 15d96a8965a5883f8c951fb9e12c572dfd66f14a Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 17:25:39 +0000 Subject: [PATCH 2/4] docs(openapi): re-generate OpenAPI specification --- api-reference/openapi_spec.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index d7f3cd41808..91785da7cb4 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -26151,6 +26151,19 @@ } } }, + "RoutingVolumeSplitResponse": { + "type": "object", + "required": [ + "split" + ], + "properties": { + "split": { + "type": "integer", + "format": "int32", + "minimum": 0 + } + } + }, "RuleConnectorSelection": { "type": "object", "description": "Represents a rule\n\n```text\nrule_name: [stripe, adyen, checkout]\n{\npayment.method = card {\npayment.method.cardtype = (credit, debit) {\npayment.method.network = (amex, rupay, diners)\n}\n\npayment.method.cardtype = credit\n}\n}\n```", From b2fc61ab92a0f9d5004cb6e81499214ebd3edd53 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 15:21:03 +0000 Subject: [PATCH 3/4] chore(cypress): run formatter and address lints --- cypress-tests/cypress/e2e/configs/Payment/Worldpayxml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress-tests/cypress/e2e/configs/Payment/Worldpayxml.js b/cypress-tests/cypress/e2e/configs/Payment/Worldpayxml.js index 590efeb478c..2f0e4068aec 100644 --- a/cypress-tests/cypress/e2e/configs/Payment/Worldpayxml.js +++ b/cypress-tests/cypress/e2e/configs/Payment/Worldpayxml.js @@ -255,7 +255,7 @@ export const connectorDetails = { }, }, }, - manualPaymentPartialRefund : { + manualPaymentPartialRefund: { Request: { amount: 5000, }, From fef59a4ea66d3666a37f295a8e85b34b97692930 Mon Sep 17 00:00:00 2001 From: Chethan Date: Tue, 27 May 2025 21:24:12 +0530 Subject: [PATCH 4/4] add api event --- crates/api_models/src/events/routing.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/api_models/src/events/routing.rs b/crates/api_models/src/events/routing.rs index 62b3b2713cf..f1ba35bd9f3 100644 --- a/crates/api_models/src/events/routing.rs +++ b/crates/api_models/src/events/routing.rs @@ -6,9 +6,10 @@ use crate::routing::{ LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig, RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind, RoutingLinkWrapper, RoutingPayloadWrapper, RoutingRetrieveLinkQuery, - RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplitResponse, - RoutingVolumeSplitWrapper, SuccessBasedRoutingConfig, SuccessBasedRoutingPayloadWrapper, - ToggleDynamicRoutingPath, ToggleDynamicRoutingQuery, ToggleDynamicRoutingWrapper, + RoutingRetrieveLinkQueryWrapper, RoutingRetrieveQuery, RoutingVolumeSplit, + RoutingVolumeSplitResponse, RoutingVolumeSplitWrapper, SuccessBasedRoutingConfig, + SuccessBasedRoutingPayloadWrapper, ToggleDynamicRoutingPath, ToggleDynamicRoutingQuery, + ToggleDynamicRoutingWrapper, }; impl ApiEventMetric for RoutingKind { @@ -146,3 +147,9 @@ impl ApiEventMetric for RoutingVolumeSplitResponse { Some(ApiEventsType::Routing) } } + +impl ApiEventMetric for RoutingVolumeSplit { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +}