mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
refactor(routing): Refactor fallback routing apis for v2 (#5592)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -3718,6 +3718,62 @@ impl BusinessProfileWrapper {
|
||||
),
|
||||
}
|
||||
}
|
||||
pub fn get_default_fallback_list_of_connector_under_profile(
|
||||
&self,
|
||||
) -> RouterResult<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
use common_utils::ext_traits::OptionExt;
|
||||
use masking::ExposeOptionInterface;
|
||||
|
||||
self.profile
|
||||
.default_fallback_routing
|
||||
.clone()
|
||||
.expose_option()
|
||||
.parse_value::<Vec<routing_types::RoutableConnectorChoice>>(
|
||||
"Vec<RoutableConnectorChoice>",
|
||||
)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Merchant default config has invalid structure")
|
||||
}
|
||||
pub fn get_default_routing_configs_from_profile(
|
||||
&self,
|
||||
) -> RouterResult<routing_types::ProfileDefaultRoutingConfig> {
|
||||
let profile_id = self.profile.profile_id.clone();
|
||||
let connectors = self.get_default_fallback_list_of_connector_under_profile()?;
|
||||
|
||||
Ok(routing_types::ProfileDefaultRoutingConfig {
|
||||
profile_id,
|
||||
connectors,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn update_default_routing_for_profile(
|
||||
self,
|
||||
db: &dyn StorageInterface,
|
||||
updated_config: &Vec<routing_types::RoutableConnectorChoice>,
|
||||
key_manager_state: &KeyManagerState,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
) -> RouterResult<()> {
|
||||
let default_fallback_routing = Secret::from(
|
||||
updated_config
|
||||
.encode_to_value()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to convert routing ref to value")?,
|
||||
);
|
||||
let business_profile_update = domain::BusinessProfileUpdate::DefaultRoutingFallbackUpdate {
|
||||
default_fallback_routing: Some(default_fallback_routing),
|
||||
};
|
||||
|
||||
db.update_business_profile_by_profile_id(
|
||||
key_manager_state,
|
||||
merchant_key_store,
|
||||
self.profile,
|
||||
business_profile_update,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to update routing algorithm ref in business profile")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn extended_card_info_toggle(
|
||||
|
||||
@ -640,7 +640,90 @@ pub async fn unlink_routing_config(
|
||||
}
|
||||
}
|
||||
|
||||
//feature update
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
pub async fn update_default_fallback_routing(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
profile_id: String,
|
||||
updated_list_of_connectors: Vec<routing_types::RoutableConnectorChoice>,
|
||||
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
metrics::ROUTING_UPDATE_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
let key_manager_state = &(&state).into();
|
||||
let profile = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
Some(&profile_id),
|
||||
merchant_account.get_id(),
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")?;
|
||||
let profile_wrapper = admin::BusinessProfileWrapper::new(profile);
|
||||
let default_list_of_connectors =
|
||||
profile_wrapper.get_default_fallback_list_of_connector_under_profile()?;
|
||||
|
||||
utils::when(
|
||||
default_list_of_connectors.len() != updated_list_of_connectors.len(),
|
||||
|| {
|
||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||
message: "current config and updated config have different lengths".to_string(),
|
||||
})
|
||||
},
|
||||
)?;
|
||||
|
||||
let existing_set_of_default_connectors: FxHashSet<String> = FxHashSet::from_iter(
|
||||
default_list_of_connectors
|
||||
.iter()
|
||||
.map(|conn_choice| conn_choice.to_string()),
|
||||
);
|
||||
let updated_set_of_default_connectors: FxHashSet<String> = FxHashSet::from_iter(
|
||||
updated_list_of_connectors
|
||||
.iter()
|
||||
.map(|conn_choice| conn_choice.to_string()),
|
||||
);
|
||||
|
||||
let symmetric_diff_between_existing_and_updated_connectors: Vec<String> =
|
||||
existing_set_of_default_connectors
|
||||
.symmetric_difference(&updated_set_of_default_connectors)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
utils::when(
|
||||
!symmetric_diff_between_existing_and_updated_connectors.is_empty(),
|
||||
|| {
|
||||
Err(errors::ApiErrorResponse::InvalidRequestData {
|
||||
message: format!(
|
||||
"connector mismatch between old and new configs ({})",
|
||||
symmetric_diff_between_existing_and_updated_connectors.join(", ")
|
||||
),
|
||||
})
|
||||
},
|
||||
)?;
|
||||
profile_wrapper
|
||||
.update_default_routing_for_profile(
|
||||
db,
|
||||
&updated_list_of_connectors,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
)
|
||||
.await?;
|
||||
|
||||
metrics::ROUTING_UPDATE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(
|
||||
updated_list_of_connectors,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
pub async fn update_default_routing_config(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
@ -693,6 +776,42 @@ pub async fn update_default_routing_config(
|
||||
Ok(service_api::ApplicationResponse::Json(updated_config))
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
pub async fn retrieve_default_fallback_algorithm_for_profile(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
key_store: domain::MerchantKeyStore,
|
||||
profile_id: String,
|
||||
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>> {
|
||||
metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||
let db = state.store.as_ref();
|
||||
let key_manager_state = &(&state).into();
|
||||
let profile = core_utils::validate_and_get_business_profile(
|
||||
db,
|
||||
key_manager_state,
|
||||
&key_store,
|
||||
Some(&profile_id),
|
||||
merchant_account.get_id(),
|
||||
)
|
||||
.await?
|
||||
.get_required_value("BusinessProfile")?;
|
||||
|
||||
let connectors_choice = admin::BusinessProfileWrapper::new(profile)
|
||||
.get_default_fallback_list_of_connector_under_profile()?;
|
||||
|
||||
metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||
Ok(service_api::ApplicationResponse::Json(connectors_choice))
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
|
||||
pub async fn retrieve_default_routing_config(
|
||||
state: SessionState,
|
||||
merchant_account: domain::MerchantAccount,
|
||||
@ -712,7 +831,6 @@ pub async fn retrieve_default_routing_config(
|
||||
service_api::ApplicationResponse::Json(conn_choice)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
|
||||
@ -1469,21 +1469,8 @@ impl BusinessProfile {
|
||||
web::scope("/{profile_id}")
|
||||
.service(
|
||||
web::resource("/fallback_routing")
|
||||
.route(web::get().to(|state, req| {
|
||||
routing::routing_retrieve_default_config(
|
||||
state,
|
||||
req,
|
||||
&TransactionType::Payment,
|
||||
)
|
||||
}))
|
||||
.route(web::post().to(|state, req, payload| {
|
||||
routing::routing_update_default_config(
|
||||
state,
|
||||
req,
|
||||
payload,
|
||||
&TransactionType::Payment,
|
||||
)
|
||||
})),
|
||||
.route(web::get().to(routing::routing_retrieve_default_config))
|
||||
.route(web::post().to(routing::routing_update_default_config)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/activate_routing_algorithm").route(web::patch().to(
|
||||
|
||||
@ -280,7 +280,55 @@ pub async fn routing_unlink_config(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
#[cfg(all(
|
||||
feature = "olap",
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn routing_update_default_config(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
json_payload: web::Json<Vec<routing_types::RoutableConnectorChoice>>,
|
||||
) -> impl Responder {
|
||||
let wrapper = routing_types::ProfileDefaultRoutingConfig {
|
||||
profile_id: path.into_inner(),
|
||||
connectors: json_payload.into_inner(),
|
||||
};
|
||||
Box::pin(oss_api::server_wrap(
|
||||
Flow::RoutingUpdateDefaultConfig,
|
||||
state,
|
||||
&req,
|
||||
wrapper,
|
||||
|state, auth: auth::AuthenticationData, wrapper, _| {
|
||||
routing::update_default_fallback_routing(
|
||||
state,
|
||||
auth.merchant_account,
|
||||
auth.key_store,
|
||||
wrapper.profile_id,
|
||||
wrapper.updated_config,
|
||||
)
|
||||
},
|
||||
#[cfg(not(feature = "release"))]
|
||||
auth::auth_type(
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth),
|
||||
&auth::JWTAuth(Permission::RoutingWrite),
|
||||
req.headers(),
|
||||
),
|
||||
#[cfg(feature = "release")]
|
||||
&auth::JWTAuth(Permission::RoutingWrite),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "olap",
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn routing_update_default_config(
|
||||
state: web::Data<AppState>,
|
||||
@ -314,7 +362,49 @@ pub async fn routing_update_default_config(
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
#[cfg(all(
|
||||
feature = "olap",
|
||||
feature = "v2",
|
||||
feature = "routing_v2",
|
||||
feature = "business_profile_v2"
|
||||
))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn routing_retrieve_default_config(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
) -> impl Responder {
|
||||
Box::pin(oss_api::server_wrap(
|
||||
Flow::RoutingRetrieveDefaultConfig,
|
||||
state,
|
||||
&req,
|
||||
path.into_inner(),
|
||||
|state, auth: auth::AuthenticationData, profile_id, _| {
|
||||
routing::retrieve_default_fallback_algorithm_for_profile(
|
||||
state,
|
||||
auth.merchant_account,
|
||||
auth.key_store,
|
||||
profile_id,
|
||||
)
|
||||
},
|
||||
#[cfg(not(feature = "release"))]
|
||||
auth::auth_type(
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth),
|
||||
&auth::JWTAuth(Permission::RoutingRead),
|
||||
req.headers(),
|
||||
),
|
||||
#[cfg(feature = "release")]
|
||||
&auth::JWTAuth(Permission::RoutingRead),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "olap",
|
||||
any(feature = "v1", feature = "v2"),
|
||||
not(any(feature = "routing_v2", feature = "business_profile_v2"))
|
||||
))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn routing_retrieve_default_config(
|
||||
state: web::Data<AppState>,
|
||||
|
||||
Reference in New Issue
Block a user