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:
Amisha Prabhat
2024-08-05 15:28:19 +05:30
committed by GitHub
parent 7743255cd8
commit 3fea00c43e
4 changed files with 168 additions and 58 deletions

View File

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

View File

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

View File

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

View File

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