mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
refactor(routing): Refactor api v2 routes for deactivating and retrieving the routing config (#5478)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Sanchith Hegde <sanchith.hegde@juspay.in> Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
@ -30,7 +30,16 @@ impl ConnectorSelection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
|
pub struct RoutingConfigRequest {
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub algorithm: RoutingAlgorithm,
|
||||||
|
pub profile_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "routing_v2")))]
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||||
pub struct RoutingConfigRequest {
|
pub struct RoutingConfigRequest {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
|||||||
@ -1,15 +1,12 @@
|
|||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod transformers;
|
pub mod transformers;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
||||||
|
use api_models::routing::RoutingConfigRequest;
|
||||||
use api_models::{
|
use api_models::{
|
||||||
enums,
|
enums,
|
||||||
routing::{
|
routing::{self as routing_types, RoutingRetrieveLinkQuery, RoutingRetrieveQuery},
|
||||||
self as routing_types, RoutingAlgorithmId, RoutingRetrieveLinkQuery, RoutingRetrieveQuery,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
|
||||||
use diesel_models::routing_algorithm::RoutingAlgorithm;
|
|
||||||
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "routing_v2")))]
|
|
||||||
use diesel_models::routing_algorithm::RoutingAlgorithm;
|
use diesel_models::routing_algorithm::RoutingAlgorithm;
|
||||||
use error_stack::ResultExt;
|
use error_stack::ResultExt;
|
||||||
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
||||||
@ -19,11 +16,8 @@ use rustc_hash::FxHashSet;
|
|||||||
use super::payments;
|
use super::payments;
|
||||||
#[cfg(feature = "payouts")]
|
#[cfg(feature = "payouts")]
|
||||||
use super::payouts;
|
use super::payouts;
|
||||||
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "routing_v2")))]
|
|
||||||
use crate::consts;
|
|
||||||
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
|
||||||
use crate::{consts, core::errors::RouterResult, db::StorageInterface};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
consts,
|
||||||
core::{
|
core::{
|
||||||
errors::{self, RouterResponse, StorageErrorExt},
|
errors::{self, RouterResponse, StorageErrorExt},
|
||||||
metrics, utils as core_utils,
|
metrics, utils as core_utils,
|
||||||
@ -36,6 +30,8 @@ use crate::{
|
|||||||
},
|
},
|
||||||
utils::{self, OptionExt, ValueExt},
|
utils::{self, OptionExt, ValueExt},
|
||||||
};
|
};
|
||||||
|
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
||||||
|
use crate::{core::errors::RouterResult, db::StorageInterface};
|
||||||
pub enum TransactionData<'a, F>
|
pub enum TransactionData<'a, F>
|
||||||
where
|
where
|
||||||
F: Clone,
|
F: Clone,
|
||||||
@ -51,10 +47,8 @@ struct RoutingAlgorithmUpdate(RoutingAlgorithm);
|
|||||||
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
||||||
impl RoutingAlgorithmUpdate {
|
impl RoutingAlgorithmUpdate {
|
||||||
pub fn create_new_routing_algorithm(
|
pub fn create_new_routing_algorithm(
|
||||||
algorithm: routing_types::RoutingAlgorithm,
|
request: &RoutingConfigRequest,
|
||||||
merchant_id: &common_utils::id_type::MerchantId,
|
merchant_id: &common_utils::id_type::MerchantId,
|
||||||
name: String,
|
|
||||||
description: String,
|
|
||||||
profile_id: String,
|
profile_id: String,
|
||||||
transaction_type: &enums::TransactionType,
|
transaction_type: &enums::TransactionType,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -67,10 +61,10 @@ impl RoutingAlgorithmUpdate {
|
|||||||
algorithm_id,
|
algorithm_id,
|
||||||
profile_id,
|
profile_id,
|
||||||
merchant_id: merchant_id.clone(),
|
merchant_id: merchant_id.clone(),
|
||||||
name,
|
name: request.name.clone(),
|
||||||
description: Some(description),
|
description: Some(request.description.clone()),
|
||||||
kind: algorithm.get_kind().foreign_into(),
|
kind: request.algorithm.get_kind().foreign_into(),
|
||||||
algorithm_data: serde_json::json!(algorithm),
|
algorithm_data: serde_json::json!(request.algorithm),
|
||||||
created_at: timestamp,
|
created_at: timestamp,
|
||||||
modified_at: timestamp,
|
modified_at: timestamp,
|
||||||
algorithm_for: transaction_type.to_owned(),
|
algorithm_for: transaction_type.to_owned(),
|
||||||
@ -150,36 +144,15 @@ pub async fn create_routing_config(
|
|||||||
state: SessionState,
|
state: SessionState,
|
||||||
merchant_account: domain::MerchantAccount,
|
merchant_account: domain::MerchantAccount,
|
||||||
key_store: domain::MerchantKeyStore,
|
key_store: domain::MerchantKeyStore,
|
||||||
request: routing_types::RoutingConfigRequest,
|
request: RoutingConfigRequest,
|
||||||
transaction_type: &enums::TransactionType,
|
transaction_type: &enums::TransactionType,
|
||||||
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||||
metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(&metrics::CONTEXT, 1, &[]);
|
metrics::ROUTING_CREATE_REQUEST_RECEIVED.add(&metrics::CONTEXT, 1, &[]);
|
||||||
let db = &*state.store;
|
let db = &*state.store;
|
||||||
let name = request
|
|
||||||
.name
|
|
||||||
.get_required_value("name")
|
|
||||||
.change_context(errors::ApiErrorResponse::MissingRequiredField { field_name: "name" })
|
|
||||||
.attach_printable("Name of config not given")?;
|
|
||||||
|
|
||||||
let description = request
|
|
||||||
.description
|
|
||||||
.get_required_value("description")
|
|
||||||
.change_context(errors::ApiErrorResponse::MissingRequiredField {
|
|
||||||
field_name: "description",
|
|
||||||
})
|
|
||||||
.attach_printable("Description of config not given")?;
|
|
||||||
|
|
||||||
let algorithm = request
|
|
||||||
.algorithm
|
|
||||||
.get_required_value("algorithm")
|
|
||||||
.change_context(errors::ApiErrorResponse::MissingRequiredField {
|
|
||||||
field_name: "algorithm",
|
|
||||||
})
|
|
||||||
.attach_printable("Algorithm of config not given")?;
|
|
||||||
|
|
||||||
let business_profile = core_utils::validate_and_get_business_profile(
|
let business_profile = core_utils::validate_and_get_business_profile(
|
||||||
db,
|
db,
|
||||||
request.profile_id.as_ref(),
|
Some(&request.profile_id),
|
||||||
merchant_account.get_id(),
|
merchant_account.get_id(),
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
@ -205,16 +178,14 @@ pub async fn create_routing_config(
|
|||||||
let algorithm_helper = helpers::RoutingAlgorithmHelpers {
|
let algorithm_helper = helpers::RoutingAlgorithmHelpers {
|
||||||
name_mca_id_set,
|
name_mca_id_set,
|
||||||
name_set,
|
name_set,
|
||||||
routing_algorithm: &algorithm,
|
routing_algorithm: &request.algorithm,
|
||||||
};
|
};
|
||||||
|
|
||||||
algorithm_helper.validate_connectors_in_routing_config()?;
|
algorithm_helper.validate_connectors_in_routing_config()?;
|
||||||
|
|
||||||
let algo = RoutingAlgorithmUpdate::create_new_routing_algorithm(
|
let algo = RoutingAlgorithmUpdate::create_new_routing_algorithm(
|
||||||
algorithm,
|
&request,
|
||||||
merchant_account.get_id(),
|
merchant_account.get_id(),
|
||||||
name,
|
|
||||||
description,
|
|
||||||
business_profile.profile_id,
|
business_profile.profile_id,
|
||||||
transaction_type,
|
transaction_type,
|
||||||
);
|
);
|
||||||
@ -327,11 +298,13 @@ pub async fn link_routing_config(
|
|||||||
let routing_algorithm =
|
let routing_algorithm =
|
||||||
RoutingAlgorithmUpdate::fetch_routing_algo(merchant_account.get_id(), &algorithm_id, db)
|
RoutingAlgorithmUpdate::fetch_routing_algo(merchant_account.get_id(), &algorithm_id, db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
utils::when(routing_algorithm.0.profile_id != profile_id, || {
|
utils::when(routing_algorithm.0.profile_id != profile_id, || {
|
||||||
Err(errors::ApiErrorResponse::PreconditionFailed {
|
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||||
message: "Profile Id is invalid for the routing config".to_string(),
|
message: "Profile Id is invalid for the routing config".to_string(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let business_profile = core_utils::validate_and_get_business_profile(
|
let business_profile = core_utils::validate_and_get_business_profile(
|
||||||
db,
|
db,
|
||||||
Some(&profile_id),
|
Some(&profile_id),
|
||||||
@ -348,6 +321,7 @@ pub async fn link_routing_config(
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
routing_algorithm.update_routing_ref_with_algorithm_id(transaction_type, &mut routing_ref)?;
|
routing_algorithm.update_routing_ref_with_algorithm_id(transaction_type, &mut routing_ref)?;
|
||||||
|
|
||||||
// TODO move to business profile
|
// TODO move to business profile
|
||||||
helpers::update_business_profile_active_algorithm_ref(
|
helpers::update_business_profile_active_algorithm_ref(
|
||||||
db,
|
db,
|
||||||
@ -432,10 +406,41 @@ pub async fn link_routing_config(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
||||||
pub async fn retrieve_routing_config(
|
pub async fn retrieve_routing_config(
|
||||||
state: SessionState,
|
state: SessionState,
|
||||||
merchant_account: domain::MerchantAccount,
|
merchant_account: domain::MerchantAccount,
|
||||||
algorithm_id: RoutingAlgorithmId,
|
algorithm_id: routing_types::RoutingAlgorithmId,
|
||||||
|
) -> RouterResponse<routing_types::MerchantRoutingAlgorithm> {
|
||||||
|
metrics::ROUTING_RETRIEVE_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||||
|
let db = state.store.as_ref();
|
||||||
|
|
||||||
|
let routing_algorithm =
|
||||||
|
RoutingAlgorithmUpdate::fetch_routing_algo(merchant_account.get_id(), &algorithm_id.0, db)
|
||||||
|
.await?;
|
||||||
|
// TODO: Move to domain types of Business Profile
|
||||||
|
core_utils::validate_and_get_business_profile(
|
||||||
|
db,
|
||||||
|
Some(&routing_algorithm.0.profile_id),
|
||||||
|
merchant_account.get_id(),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.get_required_value("BusinessProfile")
|
||||||
|
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?;
|
||||||
|
|
||||||
|
let response = routing_types::MerchantRoutingAlgorithm::foreign_try_from(routing_algorithm.0)
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("unable to parse routing algorithm")?;
|
||||||
|
|
||||||
|
metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||||
|
Ok(service_api::ApplicationResponse::Json(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "routing_v2")))]
|
||||||
|
pub async fn retrieve_routing_config(
|
||||||
|
state: SessionState,
|
||||||
|
merchant_account: domain::MerchantAccount,
|
||||||
|
algorithm_id: routing_types::RoutingAlgorithmId,
|
||||||
) -> RouterResponse<routing_types::MerchantRoutingAlgorithm> {
|
) -> RouterResponse<routing_types::MerchantRoutingAlgorithm> {
|
||||||
metrics::ROUTING_RETRIEVE_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
metrics::ROUTING_RETRIEVE_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||||
let db = state.store.as_ref();
|
let db = state.store.as_ref();
|
||||||
@ -465,6 +470,70 @@ pub async fn retrieve_routing_config(
|
|||||||
Ok(service_api::ApplicationResponse::Json(response))
|
Ok(service_api::ApplicationResponse::Json(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "v2", feature = "routing_v2"))]
|
||||||
|
pub async fn unlink_routing_config(
|
||||||
|
state: SessionState,
|
||||||
|
merchant_account: domain::MerchantAccount,
|
||||||
|
profile_id: String,
|
||||||
|
transaction_type: &enums::TransactionType,
|
||||||
|
) -> RouterResponse<routing_types::RoutingDictionaryRecord> {
|
||||||
|
metrics::ROUTING_UNLINK_CONFIG.add(&metrics::CONTEXT, 1, &[]);
|
||||||
|
let db = state.store.as_ref();
|
||||||
|
|
||||||
|
let business_profile = core_utils::validate_and_get_business_profile(
|
||||||
|
db,
|
||||||
|
Some(&profile_id),
|
||||||
|
merchant_account.get_id(),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.get_required_value("BusinessProfile")?;
|
||||||
|
|
||||||
|
let routing_algo_ref = routing_types::RoutingAlgorithmRef::parse_routing_algorithm(
|
||||||
|
match transaction_type {
|
||||||
|
enums::TransactionType::Payment => business_profile.routing_algorithm.clone(),
|
||||||
|
#[cfg(feature = "payouts")]
|
||||||
|
enums::TransactionType::Payout => business_profile.payout_routing_algorithm.clone(),
|
||||||
|
}
|
||||||
|
.map(Secret::new),
|
||||||
|
)
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("unable to deserialize routing algorithm ref from merchant account")?
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if let Some(algorithm_id) = routing_algo_ref.algorithm_id {
|
||||||
|
let timestamp = common_utils::date_time::now_unix_timestamp();
|
||||||
|
let routing_algorithm: routing_types::RoutingAlgorithmRef =
|
||||||
|
routing_types::RoutingAlgorithmRef {
|
||||||
|
algorithm_id: None,
|
||||||
|
timestamp,
|
||||||
|
config_algo_id: routing_algo_ref.config_algo_id.clone(),
|
||||||
|
surcharge_config_algo_id: routing_algo_ref.surcharge_config_algo_id,
|
||||||
|
};
|
||||||
|
let record = RoutingAlgorithmUpdate::fetch_routing_algo(
|
||||||
|
merchant_account.get_id(),
|
||||||
|
&algorithm_id,
|
||||||
|
db,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let response = record.0.foreign_into();
|
||||||
|
helpers::update_business_profile_active_algorithm_ref(
|
||||||
|
db,
|
||||||
|
business_profile,
|
||||||
|
routing_algorithm,
|
||||||
|
transaction_type,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]);
|
||||||
|
Ok(service_api::ApplicationResponse::Json(response))
|
||||||
|
} else {
|
||||||
|
Err(errors::ApiErrorResponse::PreconditionFailed {
|
||||||
|
message: "Algorithm is already inactive".to_string(),
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "routing_v2")))]
|
||||||
pub async fn unlink_routing_config(
|
pub async fn unlink_routing_config(
|
||||||
state: SessionState,
|
state: SessionState,
|
||||||
merchant_account: domain::MerchantAccount,
|
merchant_account: domain::MerchantAccount,
|
||||||
|
|||||||
@ -1455,18 +1455,16 @@ impl BusinessProfile {
|
|||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.service(
|
.service(web::resource("/deactivate_routing_algorithm").route(
|
||||||
web::resource("/deactivate_routing_algorithm").route(web::post().to(
|
web::patch().to(|state, req, path| {
|
||||||
|state, req, path| {
|
routing::routing_unlink_config(
|
||||||
routing::routing_unlink_config(
|
state,
|
||||||
state,
|
req,
|
||||||
req,
|
path,
|
||||||
path,
|
&TransactionType::Payment,
|
||||||
&TransactionType::Payment,
|
)
|
||||||
)
|
}),
|
||||||
},
|
)),
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -200,7 +200,41 @@ pub async fn list_routing_configs(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(all(feature = "olap", feature = "v2", feature = "routing_v2"))]
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub async fn routing_unlink_config(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
path: web::Path<String>,
|
||||||
|
transaction_type: &enums::TransactionType,
|
||||||
|
) -> impl Responder {
|
||||||
|
let flow = Flow::RoutingUnlinkConfig;
|
||||||
|
Box::pin(oss_api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state,
|
||||||
|
&req,
|
||||||
|
path.into_inner(),
|
||||||
|
|state, auth: auth::AuthenticationData, path, _| {
|
||||||
|
routing::unlink_routing_config(state, auth.merchant_account, path, transaction_type)
|
||||||
|
},
|
||||||
|
#[cfg(not(feature = "release"))]
|
||||||
|
auth::auth_type(
|
||||||
|
&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(feature = "routing_v2")
|
||||||
|
))]
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn routing_unlink_config(
|
pub async fn routing_unlink_config(
|
||||||
state: web::Data<AppState>,
|
state: web::Data<AppState>,
|
||||||
|
|||||||
Reference in New Issue
Block a user