diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 39e5483700a5..3b437b703bef 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -146,3 +146,6 @@ pub const CONNECTOR_TRANSACTION_ID_HASH_BYTES: usize = 25; /// Apple Pay validation url pub const APPLEPAY_VALIDATION_URL: &str = "https://apple-pay-gateway-cert.apple.com/paymentservices/startSession"; + +/// Request ID +pub const X_REQUEST_ID: &str = "x-request-id"; diff --git a/crates/external_services/src/grpc_client.rs b/crates/external_services/src/grpc_client.rs index 8981a1094d69..404685025ed3 100644 --- a/crates/external_services/src/grpc_client.rs +++ b/crates/external_services/src/grpc_client.rs @@ -6,6 +6,8 @@ pub mod dynamic_routing; pub mod health_check_client; use std::{fmt::Debug, sync::Arc}; +#[cfg(feature = "dynamic_routing")] +use common_utils::consts; #[cfg(feature = "dynamic_routing")] use dynamic_routing::{DynamicRoutingClientConfig, RoutingStrategy}; #[cfg(feature = "dynamic_routing")] @@ -16,6 +18,8 @@ use http_body_util::combinators::UnsyncBoxBody; use hyper::body::Bytes; #[cfg(feature = "dynamic_routing")] use hyper_util::client::legacy::connect::HttpConnector; +#[cfg(feature = "dynamic_routing")] +use router_env::logger; use serde; #[cfg(feature = "dynamic_routing")] use tonic::Status; @@ -76,3 +80,51 @@ impl GrpcClientSettings { }) } } + +/// Contains grpc headers +#[derive(Debug)] +pub struct GrpcHeaders { + /// Tenant id + pub tenant_id: String, + /// Request id + pub request_id: Option, +} + +#[cfg(feature = "dynamic_routing")] +/// Trait to add necessary headers to the tonic Request +pub(crate) trait AddHeaders { + /// Add necessary header fields to the tonic Request + fn add_headers_to_grpc_request(&mut self, headers: GrpcHeaders); +} + +#[cfg(feature = "dynamic_routing")] +impl AddHeaders for tonic::Request { + #[track_caller] + fn add_headers_to_grpc_request(&mut self, headers: GrpcHeaders) { + headers.tenant_id + .parse() + .map(|tenant_id| { + self + .metadata_mut() + .append(consts::TENANT_HEADER, tenant_id) + }) + .inspect_err( + |err| logger::warn!(header_parse_error=?err,"invalid {} received",consts::TENANT_HEADER), + ) + .ok(); + + headers.request_id.map(|request_id| { + request_id + .parse() + .map(|request_id| { + self + .metadata_mut() + .append(consts::X_REQUEST_ID, request_id) + }) + .inspect_err( + |err| logger::warn!(header_parse_error=?err,"invalid {} received",consts::X_REQUEST_ID), + ) + .ok(); + }); + } +} diff --git a/crates/external_services/src/grpc_client/dynamic_routing/elimination_rate_client.rs b/crates/external_services/src/grpc_client/dynamic_routing/elimination_rate_client.rs index 6587b7941f4c..bc5ce4997270 100644 --- a/crates/external_services/src/grpc_client/dynamic_routing/elimination_rate_client.rs +++ b/crates/external_services/src/grpc_client/dynamic_routing/elimination_rate_client.rs @@ -21,6 +21,7 @@ pub mod elimination_rate { } use super::{Client, DynamicRoutingError, DynamicRoutingResult}; +use crate::grpc_client::{AddHeaders, GrpcHeaders}; /// The trait Elimination Based Routing would have the functions required to support performance, calculation and invalidation bucket #[async_trait::async_trait] @@ -32,6 +33,7 @@ pub trait EliminationBasedRouting: dyn_clone::DynClone + Send + Sync { params: String, labels: Vec, configs: Option, + headers: GrpcHeaders, ) -> DynamicRoutingResult; /// To update the bucket size and ttl for list of connectors with its respective bucket name async fn update_elimination_bucket_config( @@ -40,11 +42,13 @@ pub trait EliminationBasedRouting: dyn_clone::DynClone + Send + Sync { params: String, report: Vec, config: Option, + headers: GrpcHeaders, ) -> DynamicRoutingResult; /// To invalidate the previous id's bucket async fn invalidate_elimination_bucket( &self, id: String, + headers: GrpcHeaders, ) -> DynamicRoutingResult; } @@ -56,6 +60,7 @@ impl EliminationBasedRouting for EliminationAnalyserClient { params: String, label_input: Vec, configs: Option, + headers: GrpcHeaders, ) -> DynamicRoutingResult { let labels = label_input .into_iter() @@ -64,13 +69,15 @@ impl EliminationBasedRouting for EliminationAnalyserClient { let config = configs.map(ForeignTryFrom::foreign_try_from).transpose()?; - let request = tonic::Request::new(EliminationRequest { + let mut request = tonic::Request::new(EliminationRequest { id, params, labels, config, }); + request.add_headers_to_grpc_request(headers); + let response = self .clone() .get_elimination_status(request) @@ -89,6 +96,7 @@ impl EliminationBasedRouting for EliminationAnalyserClient { params: String, report: Vec, configs: Option, + headers: GrpcHeaders, ) -> DynamicRoutingResult { let config = configs.map(ForeignTryFrom::foreign_try_from).transpose()?; @@ -102,13 +110,15 @@ impl EliminationBasedRouting for EliminationAnalyserClient { }) .collect::>(); - let request = tonic::Request::new(UpdateEliminationBucketRequest { + let mut request = tonic::Request::new(UpdateEliminationBucketRequest { id, params, labels_with_bucket_name, config, }); + request.add_headers_to_grpc_request(headers); + let response = self .clone() .update_elimination_bucket(request) @@ -122,8 +132,11 @@ impl EliminationBasedRouting for EliminationAnalyserClient { async fn invalidate_elimination_bucket( &self, id: String, + headers: GrpcHeaders, ) -> DynamicRoutingResult { - let request = tonic::Request::new(InvalidateBucketRequest { id }); + let mut request = tonic::Request::new(InvalidateBucketRequest { id }); + + request.add_headers_to_grpc_request(headers); let response = self .clone() diff --git a/crates/external_services/src/grpc_client/dynamic_routing/success_rate_client.rs b/crates/external_services/src/grpc_client/dynamic_routing/success_rate_client.rs index f6d3efb88769..3cf06ab63beb 100644 --- a/crates/external_services/src/grpc_client/dynamic_routing/success_rate_client.rs +++ b/crates/external_services/src/grpc_client/dynamic_routing/success_rate_client.rs @@ -21,6 +21,7 @@ pub mod success_rate { tonic::include_proto!("success_rate"); } use super::{Client, DynamicRoutingError, DynamicRoutingResult}; +use crate::grpc_client::{AddHeaders, GrpcHeaders}; /// The trait Success Based Dynamic Routing would have the functions required to support the calculation and updation window #[async_trait::async_trait] pub trait SuccessBasedDynamicRouting: dyn_clone::DynClone + Send + Sync { @@ -31,6 +32,7 @@ pub trait SuccessBasedDynamicRouting: dyn_clone::DynClone + Send + Sync { success_rate_based_config: SuccessBasedRoutingConfig, params: String, label_input: Vec, + headers: GrpcHeaders, ) -> DynamicRoutingResult; /// To update the success rate with the given label async fn update_success_rate( @@ -39,11 +41,13 @@ pub trait SuccessBasedDynamicRouting: dyn_clone::DynClone + Send + Sync { success_rate_based_config: SuccessBasedRoutingConfig, params: String, response: Vec, + headers: GrpcHeaders, ) -> DynamicRoutingResult; /// To invalidates the success rate routing keys async fn invalidate_success_rate_routing_keys( &self, id: String, + headers: GrpcHeaders, ) -> DynamicRoutingResult; } @@ -55,6 +59,7 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient { success_rate_based_config: SuccessBasedRoutingConfig, params: String, label_input: Vec, + headers: GrpcHeaders, ) -> DynamicRoutingResult { let labels = label_input .into_iter() @@ -66,13 +71,15 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient { .map(ForeignTryFrom::foreign_try_from) .transpose()?; - let request = tonic::Request::new(CalSuccessRateRequest { + let mut request = tonic::Request::new(CalSuccessRateRequest { id, params, labels, config, }); + request.add_headers_to_grpc_request(headers); + let response = self .clone() .fetch_success_rate(request) @@ -91,6 +98,7 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient { success_rate_based_config: SuccessBasedRoutingConfig, params: String, label_input: Vec, + headers: GrpcHeaders, ) -> DynamicRoutingResult { let config = success_rate_based_config .config @@ -105,13 +113,15 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient { }) .collect(); - let request = tonic::Request::new(UpdateSuccessRateWindowRequest { + let mut request = tonic::Request::new(UpdateSuccessRateWindowRequest { id, params, labels_with_status, config, }); + request.add_headers_to_grpc_request(headers); + let response = self .clone() .update_success_rate_window(request) @@ -126,8 +136,11 @@ impl SuccessBasedDynamicRouting for SuccessRateCalculatorClient { async fn invalidate_success_rate_routing_keys( &self, id: String, + headers: GrpcHeaders, ) -> DynamicRoutingResult { - let request = tonic::Request::new(InvalidateWindowsRequest { id }); + let mut request = tonic::Request::new(InvalidateWindowsRequest { id }); + + request.add_headers_to_grpc_request(headers); let response = self .clone() diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 746b6b7fb409..6d3c0973283c 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -1343,6 +1343,7 @@ pub async fn perform_success_based_routing( success_based_routing_configs, success_based_routing_config_params, routable_connectors, + state.get_grpc_headers(), ) .await .change_context(errors::RoutingError::SuccessRateCalculationError) diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 36bcb233d02a..5fc54fe96e52 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -1408,7 +1408,10 @@ pub async fn success_based_routing_update_configs( .as_ref() .async_map(|sr_client| async { sr_client - .invalidate_success_rate_routing_keys(prefix_of_dynamic_routing_keys) + .invalidate_success_rate_routing_keys( + prefix_of_dynamic_routing_keys, + state.get_grpc_headers(), + ) .await .change_context(errors::ApiErrorResponse::GenericNotFoundError { message: "Failed to invalidate the routing keys".to_string(), diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 2abb5e64cd4b..489c3122a8e9 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -719,6 +719,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing( success_based_routing_configs.clone(), success_based_routing_config_params.clone(), routable_connectors.clone(), + state.get_grpc_headers(), ) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -855,6 +856,7 @@ pub async fn push_metrics_with_update_window_for_success_based_routing( }, payment_status_attribute == common_enums::AttemptStatus::Charged, )], + state.get_grpc_headers(), ) .await .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 0b5d481e3da3..2decddb806cb 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -12,7 +12,10 @@ use common_utils::id_type; use external_services::email::{ no_email::NoEmailClient, ses::AwsSes, smtp::SmtpServer, EmailClientConfigs, EmailService, }; -use external_services::{file_storage::FileStorageInterface, grpc_client::GrpcClients}; +use external_services::{ + file_storage::FileStorageInterface, + grpc_client::{GrpcClients, GrpcHeaders}, +}; use hyperswitch_interfaces::{ encryption_interface::EncryptionManagementInterface, secrets_interface::secret_state::{RawSecret, SecuredSecret}, @@ -119,6 +122,12 @@ impl SessionState { event_context: events::EventContext::new(self.event_handler.clone()), } } + pub fn get_grpc_headers(&self) -> GrpcHeaders { + GrpcHeaders { + tenant_id: self.tenant.tenant_id.get_string_repr().to_string(), + request_id: self.request_id.map(|req_id| (*req_id).to_string()), + } + } } pub trait SessionStateInfo {