diff --git a/CHANGELOG.md b/CHANGELOG.md index 117d4cd90e24..65f8237af54e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,32 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.02.02.0 + +### Features + +- **configs:** [Noon] Add applepay mandate configs ([#3508](https://github.com/juspay/hyperswitch/pull/3508)) ([`7cf6c8c`](https://github.com/juspay/hyperswitch/commit/7cf6c8c0b9c4042f2e6b9277b7c75c85546821f7)) +- Add deep health check for scheduler ([#3304](https://github.com/juspay/hyperswitch/pull/3304)) ([`170e10c`](https://github.com/juspay/hyperswitch/commit/170e10cb8e0880737585284dd43437f549c019d3)) +- Add healthcheck for outgoing request ([#3519](https://github.com/juspay/hyperswitch/pull/3519)) ([`54fb61e`](https://github.com/juspay/hyperswitch/commit/54fb61eeebec503f599774fe9e97f6b6ce3f1458)) + +### Bug Fixes + +- **core:** Fix mandate_details to store some value only if mandate_data struct is present ([#3525](https://github.com/juspay/hyperswitch/pull/3525)) ([`78fdad2`](https://github.com/juspay/hyperswitch/commit/78fdad218ca3ae3c7410dfb8a7a8a5e542adff1c)) +- **logging:** Add an end log line for `LogSpanInitializer` ([#3528](https://github.com/juspay/hyperswitch/pull/3528)) ([`13be7e6`](https://github.com/juspay/hyperswitch/commit/13be7e6f8771a1128e3c0c5b189c91d9a0dd1416)) + +### Refactors + +- **connector:** [CYBERSOURCE] Remove default case for Cybersource ([#2705](https://github.com/juspay/hyperswitch/pull/2705)) ([`1828ea6`](https://github.com/juspay/hyperswitch/commit/1828ea6187c46d9c18dc8a0b5224387403b998e2)) + +### Miscellaneous Tasks + +- **postman:** Update Postman collection files ([`1deb37e`](https://github.com/juspay/hyperswitch/commit/1deb37ebd1128041ded64a4966a2d47a61e8c499)) +- Add file storage config in env_specific toml ([#3512](https://github.com/juspay/hyperswitch/pull/3512)) ([`20efc30`](https://github.com/juspay/hyperswitch/commit/20efc3020ac389199eed13154f070685417ef82a)) + +**Full Changelog:** [`2024.02.01.0...2024.02.02.0`](https://github.com/juspay/hyperswitch/compare/2024.02.01.0...2024.02.02.0) + +- - - + ## 2024.02.01.0 ### Features diff --git a/Cargo.lock b/Cargo.lock index a1aabc89a040..f0334ce9cfc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5526,6 +5526,7 @@ dependencies = [ "external_services", "futures 0.3.28", "masking", + "num_cpus", "once_cell", "rand 0.8.5", "redis_interface", diff --git a/config/config.example.toml b/config/config.example.toml index f7e9fa70f6e3..87999f0e9e93 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -298,6 +298,12 @@ lower_fetch_limit = 1800 # Lower limit for fetching entries from redis lock_key = "PRODUCER_LOCKING_KEY" # The following keys defines the producer lock that is created in redis with lock_ttl = 160 # the ttl being the expiry (in seconds) +# Scheduler server configuration +[scheduler.server] +port = 3000 # Port on which the server will listen for incoming requests +host = "127.0.0.1" # Host IP address to bind the server to +workers = 1 # Number of actix workers to handle incoming requests concurrently + batch_size = 200 # Specifies the batch size the producer will push under a single entry in the redis queue # Drainer configuration, which handles draining raw SQL queries from Redis streams to the SQL database @@ -383,6 +389,7 @@ bank_debit.becs = { connector_list = "gocardless" } # Mandate supported payment bank_debit.sepa = { connector_list = "gocardless" } # Mandate supported payment method type and connector for bank_debit bank_redirect.ideal = {connector_list = "stripe,adyen,globalpay"} # Mandate supported payment method type and connector for bank_redirect bank_redirect.sofort = {connector_list = "stripe,adyen,globalpay"} +wallet.apple_pay = { connector_list = "stripe,adyen,cybersource,noon" } # Required fields info used while listing the payment_method_data diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index b84546eff34f..2c5d16e3e3c7 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -114,7 +114,7 @@ bank_debit.sepa.connector_list = "gocardless" card.credit.connector_list = "stripe,adyen,authorizedotnet,cybersource,globalpay,worldpay,multisafepay,nmi,nexinets,noon" card.debit.connector_list = "stripe,adyen,authorizedotnet,cybersource,globalpay,worldpay,multisafepay,nmi,nexinets,noon" pay_later.klarna.connector_list = "adyen" -wallet.apple_pay.connector_list = "stripe,adyen,cybersource" +wallet.apple_pay.connector_list = "stripe,adyen,cybersource,noon" wallet.google_pay.connector_list = "stripe,adyen,cybersource" wallet.paypal.connector_list = "adyen" bank_redirect.ideal = {connector_list = "stripe,adyen,globalpay"} diff --git a/config/deployments/production.toml b/config/deployments/production.toml index d5479b4f02c1..964281c52bba 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -114,7 +114,7 @@ bank_debit.sepa.connector_list = "gocardless" card.credit.connector_list = "stripe,adyen,authorizedotnet,cybersource,globalpay,worldpay,multisafepay,nmi,nexinets,noon" card.debit.connector_list = "stripe,adyen,authorizedotnet,cybersource,globalpay,worldpay,multisafepay,nmi,nexinets,noon" pay_later.klarna.connector_list = "adyen" -wallet.apple_pay.connector_list = "stripe,adyen,cybersource" +wallet.apple_pay.connector_list = "stripe,adyen,cybersource,noon" wallet.google_pay.connector_list = "stripe,adyen,cybersource" wallet.paypal.connector_list = "adyen" bank_redirect.ideal = {connector_list = "stripe,adyen,globalpay"} diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 14f49e01caf9..aa2377cf8a08 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -114,7 +114,7 @@ bank_debit.sepa.connector_list = "gocardless" card.credit.connector_list = "stripe,adyen,authorizedotnet,cybersource,globalpay,worldpay,multisafepay,nmi,nexinets,noon" card.debit.connector_list = "stripe,adyen,authorizedotnet,cybersource,globalpay,worldpay,multisafepay,nmi,nexinets,noon" pay_later.klarna.connector_list = "adyen" -wallet.apple_pay.connector_list = "stripe,adyen,cybersource" +wallet.apple_pay.connector_list = "stripe,adyen,cybersource,noon" wallet.google_pay.connector_list = "stripe,adyen,cybersource" wallet.paypal.connector_list = "adyen" bank_redirect.ideal = {connector_list = "stripe,adyen,globalpay"} diff --git a/config/deployments/scheduler/consumer.toml b/config/deployments/scheduler/consumer.toml index 907e3b8297e3..cdd605526689 100644 --- a/config/deployments/scheduler/consumer.toml +++ b/config/deployments/scheduler/consumer.toml @@ -9,3 +9,9 @@ stream = "scheduler_stream" [scheduler.consumer] consumer_group = "scheduler_group" disabled = false # This flag decides if the consumer should actively consume task + +# Scheduler server configuration +[scheduler.server] +port = 3000 # Port on which the server will listen for incoming requests +host = "127.0.0.1" # Host IP address to bind the server to +workers = 1 # Number of actix workers to handle incoming requests concurrently diff --git a/config/deployments/scheduler/producer.toml b/config/deployments/scheduler/producer.toml index 579466a23cc8..9cbaee96f03c 100644 --- a/config/deployments/scheduler/producer.toml +++ b/config/deployments/scheduler/producer.toml @@ -12,3 +12,9 @@ lock_key = "producer_locking_key" # The following keys defines the producer lock lock_ttl = 160 # the ttl being the expiry (in seconds) lower_fetch_limit = 900 # Lower limit for fetching entries from redis queue (in seconds) upper_fetch_limit = 0 # Upper limit for fetching entries from the redis queue (in seconds)0 + +# Scheduler server configuration +[scheduler.server] +port = 3000 # Port on which the server will listen for incoming requests +host = "127.0.0.1" # Host IP address to bind the server to +workers = 1 # Number of actix workers to handle incoming requests concurrently diff --git a/config/development.toml b/config/development.toml index 584bdf751a2b..20abb7bd6f30 100644 --- a/config/development.toml +++ b/config/development.toml @@ -228,6 +228,11 @@ stream = "SCHEDULER_STREAM" disabled = false consumer_group = "SCHEDULER_GROUP" +[scheduler.server] +port = 3000 +host = "127.0.0.1" +workers = 1 + [email] sender_email = "example@example.com" aws_region = "" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 67fca85929d4..e6dc01afa741 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -227,6 +227,11 @@ stream = "SCHEDULER_STREAM" disabled = false consumer_group = "SCHEDULER_GROUP" +[scheduler.server] +port = 3000 +host = "127.0.0.1" +workers = 1 + #tokenization configuration which describe token lifetime and payment method for specific connector [tokenization] stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { type = "disable_only", list = "google_pay" } } @@ -334,7 +339,7 @@ adyen = { banks = "aib,bank_of_scotland,danske_bank,first_direct,first_trust,hal [mandates.supported_payment_methods] pay_later.klarna = {connector_list = "adyen"} wallet.google_pay = {connector_list = "stripe,adyen"} -wallet.apple_pay = {connector_list = "stripe,adyen"} +wallet.apple_pay = {connector_list = "stripe,adyen,cybersource,noon"} wallet.paypal = {connector_list = "adyen"} card.credit = {connector_list = "stripe,adyen,authorizedotnet,cybersource,globalpay,worldpay,multisafepay,nmi,nexinets,noon"} card.debit = {connector_list = "stripe,adyen,authorizedotnet,cybersource,globalpay,worldpay,multisafepay,nmi,nexinets,noon"} diff --git a/crates/api_models/src/health_check.rs b/crates/api_models/src/health_check.rs index 8323f1351346..e4611f43bcf8 100644 --- a/crates/api_models/src/health_check.rs +++ b/crates/api_models/src/health_check.rs @@ -5,6 +5,14 @@ pub struct RouterHealthCheckResponse { pub locker: bool, #[cfg(feature = "olap")] pub analytics: bool, + pub outgoing_request: bool, } impl common_utils::events::ApiEventMetric for RouterHealthCheckResponse {} +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct SchedulerHealthCheckResponse { + pub database: bool, + pub redis: bool, +} + +impl common_utils::events::ApiEventMetric for SchedulerHealthCheckResponse {} diff --git a/crates/router/src/bin/scheduler.rs b/crates/router/src/bin/scheduler.rs index b800ecb897e5..5f98cd880141 100644 --- a/crates/router/src/bin/scheduler.rs +++ b/crates/router/src/bin/scheduler.rs @@ -1,21 +1,29 @@ #![recursion_limit = "256"] use std::{str::FromStr, sync::Arc}; +use actix_web::{dev::Server, web, Scope}; +use api_models::health_check::SchedulerHealthCheckResponse; use common_utils::ext_traits::{OptionExt, StringExt}; use diesel_models::process_tracker as storage; use error_stack::ResultExt; use router::{ configs::settings::{CmdLineConf, Settings}, - core::errors::{self, CustomResult}, - logger, routes, services, + core::{ + errors::{self, CustomResult}, + health_check::HealthCheckInterface, + }, + logger, routes, + services::{self, api}, types::storage::ProcessTrackerExt, workflows, }; +use router_env::{instrument, tracing}; use scheduler::{ consumer::workflows::ProcessTrackerWorkflow, errors::ProcessTrackerError, workflows::ProcessTrackerWorkflows, SchedulerAppState, }; use serde::{Deserialize, Serialize}; +use storage_impl::errors::ApplicationError; use strum::EnumString; use tokio::sync::{mpsc, oneshot}; @@ -68,6 +76,19 @@ async fn main() -> CustomResult<(), ProcessTrackerError> { [router_env::service_name!()], ); + #[allow(clippy::expect_used)] + let web_server = Box::pin(start_web_server( + state.clone(), + scheduler_flow_str.to_string(), + )) + .await + .expect("Failed to create the server"); + + tokio::spawn(async move { + let _ = web_server.await; + logger::error!("The health check probe stopped working!"); + }); + logger::debug!(startup_config=?state.conf); start_scheduler(&state, scheduler_flow, (tx, rx)).await?; @@ -76,6 +97,106 @@ async fn main() -> CustomResult<(), ProcessTrackerError> { Ok(()) } +pub async fn start_web_server( + state: routes::AppState, + service: String, +) -> errors::ApplicationResult { + let server = state + .conf + .scheduler + .as_ref() + .ok_or(ApplicationError::InvalidConfigurationValueError( + "Scheduler server is invalidly configured".into(), + ))? + .server + .clone(); + + let web_server = actix_web::HttpServer::new(move || { + actix_web::App::new().service(Health::server(state.clone(), service.clone())) + }) + .bind((server.host.as_str(), server.port))? + .workers(server.workers) + .run(); + let _ = web_server.handle(); + + Ok(web_server) +} + +pub struct Health; + +impl Health { + pub fn server(state: routes::AppState, service: String) -> Scope { + web::scope("health") + .app_data(web::Data::new(state)) + .app_data(web::Data::new(service)) + .service(web::resource("").route(web::get().to(health))) + .service(web::resource("/ready").route(web::get().to(deep_health_check))) + } +} + +#[instrument(skip_all)] +pub async fn health() -> impl actix_web::Responder { + logger::info!("Scheduler health was called"); + actix_web::HttpResponse::Ok().body("Scheduler health is good") +} +#[instrument(skip_all)] +pub async fn deep_health_check( + state: web::Data, + service: web::Data, +) -> impl actix_web::Responder { + let report = deep_health_check_func(state, service).await; + match report { + Ok(response) => services::http_response_json( + serde_json::to_string(&response) + .map_err(|err| { + logger::error!(serialization_error=?err); + }) + .unwrap_or_default(), + ), + Err(err) => api::log_and_return_error_response(err), + } +} +#[instrument(skip_all)] +pub async fn deep_health_check_func( + state: web::Data, + service: web::Data, +) -> errors::RouterResult { + logger::info!("{} deep health check was called", service.into_inner()); + + logger::debug!("Database health check begin"); + + let db_status = state.health_check_db().await.map(|_| true).map_err(|err| { + error_stack::report!(errors::ApiErrorResponse::HealthCheckError { + component: "Database", + message: err.to_string() + }) + })?; + + logger::debug!("Database health check end"); + + logger::debug!("Redis health check begin"); + + let redis_status = state + .health_check_redis() + .await + .map(|_| true) + .map_err(|err| { + error_stack::report!(errors::ApiErrorResponse::HealthCheckError { + component: "Redis", + message: err.to_string() + }) + })?; + + logger::debug!("Redis health check end"); + + let response = SchedulerHealthCheckResponse { + database: db_status, + redis: redis_status, + }; + + Ok(response) +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, EnumString)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[strum(serialize_all = "SCREAMING_SNAKE_CASE")] diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 9b0bf61c5458..324fe77d0bc5 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -193,9 +193,22 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest { utils::get_unimplemented_payment_method_error_message("Cybersource"), ))?, }, - _ => Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("Cybersource"), - ))?, + payments::PaymentMethodData::CardRedirect(_) + | payments::PaymentMethodData::PayLater(_) + | payments::PaymentMethodData::BankRedirect(_) + | payments::PaymentMethodData::BankDebit(_) + | payments::PaymentMethodData::BankTransfer(_) + | payments::PaymentMethodData::Crypto(_) + | payments::PaymentMethodData::MandatePayment + | payments::PaymentMethodData::Reward + | payments::PaymentMethodData::Upi(_) + | payments::PaymentMethodData::Voucher(_) + | payments::PaymentMethodData::GiftCard(_) + | payments::PaymentMethodData::CardToken(_) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Cybersource"), + ))? + } }; let processing_information = ProcessingInformation { diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 12b688e3d3ab..3c3f01dc5f97 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -91,3 +91,6 @@ pub const MAX_SESSION_EXPIRY: u32 = 7890000; pub const MIN_SESSION_EXPIRY: u32 = 60; pub const LOCKER_HEALTH_CALL_PATH: &str = "/health"; + +// URL for checking the outgoing call +pub const OUTGOING_CALL_URL: &str = "https://api.stripe.com/healthcheck"; diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index cbc4290f63bb..9052893d4a96 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -186,6 +186,12 @@ pub enum ConnectorError { InvalidConnectorConfig { config: &'static str }, } +#[derive(Debug, thiserror::Error)] +pub enum HealthCheckOutGoing { + #[error("Outgoing call failed with error: {message}")] + OutGoingFailed { message: String }, +} + #[derive(Debug, thiserror::Error)] pub enum VaultError { #[error("Failed to save card in card vault")] diff --git a/crates/router/src/core/errors/api_error_response.rs b/crates/router/src/core/errors/api_error_response.rs index 023e1f4b7fb3..e83483b08169 100644 --- a/crates/router/src/core/errors/api_error_response.rs +++ b/crates/router/src/core/errors/api_error_response.rs @@ -238,7 +238,7 @@ pub enum ApiErrorResponse { WebhookInvalidMerchantSecret, #[error(error_type = ErrorType::InvalidRequestError, code = "IR_19", message = "{message}")] CurrencyNotSupported { message: String }, - #[error(error_type = ErrorType::ServerNotAvailable, code= "HE_00", message = "{component} health check is failiing with error: {message}")] + #[error(error_type = ErrorType::ServerNotAvailable, code= "HE_00", message = "{component} health check is failing with error: {message}")] HealthCheckError { component: &'static str, message: String, diff --git a/crates/router/src/core/health_check.rs b/crates/router/src/core/health_check.rs index 6fc038b82e91..bc523b4fba66 100644 --- a/crates/router/src/core/health_check.rs +++ b/crates/router/src/core/health_check.rs @@ -4,7 +4,7 @@ use error_stack::ResultExt; use router_env::logger; use crate::{ - consts::LOCKER_HEALTH_CALL_PATH, + consts, core::errors::{self, CustomResult}, routes::app, services::api as services, @@ -15,6 +15,7 @@ pub trait HealthCheckInterface { async fn health_check_db(&self) -> CustomResult<(), errors::HealthCheckDBError>; async fn health_check_redis(&self) -> CustomResult<(), errors::HealthCheckRedisError>; async fn health_check_locker(&self) -> CustomResult<(), errors::HealthCheckLockerError>; + async fn health_check_outgoing(&self) -> CustomResult<(), errors::HealthCheckOutGoing>; #[cfg(feature = "olap")] async fn health_check_analytics(&self) -> CustomResult<(), errors::HealthCheckDBError>; } @@ -61,7 +62,7 @@ impl HealthCheckInterface for app::AppState { let locker = &self.conf.locker; if !locker.mock_locker { let mut url = locker.host_rs.to_owned(); - url.push_str(LOCKER_HEALTH_CALL_PATH); + url.push_str(consts::LOCKER_HEALTH_CALL_PATH); let request = services::Request::new(services::Method::Get, &url); services::call_connector_api(self, request) .await @@ -108,4 +109,22 @@ impl HealthCheckInterface for app::AppState { } } } + + async fn health_check_outgoing(&self) -> CustomResult<(), errors::HealthCheckOutGoing> { + let request = services::Request::new(services::Method::Get, consts::OUTGOING_CALL_URL); + services::call_connector_api(self, request) + .await + .map_err(|err| errors::HealthCheckOutGoing::OutGoingFailed { + message: err.to_string(), + })? + .map_err(|err| errors::HealthCheckOutGoing::OutGoingFailed { + message: format!( + "Got a non 200 status while making outgoing request. Error {:?}", + err.response + ), + })?; + + logger::debug!("Outgoing request successful"); + Ok(()) + } } diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index e08dbc61f5e7..5797fd60da00 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -1823,7 +1823,6 @@ pub async fn list_payment_methods( } else { api_surcharge_decision_configs::MerchantSurchargeConfigs::default() }; - print!("PAMT{:?}", payment_attempt); Ok(services::ApplicationResponse::Json( api::PaymentMethodListResponse { redirect_url: merchant_account.return_url, @@ -2070,13 +2069,30 @@ pub async fn filter_payment_methods( })?; let filter7 = payment_attempt .and_then(|attempt| attempt.mandate_details.as_ref()) - .map(|_mandate_details| { - filter_pm_based_on_supported_payments_for_mandate( - supported_payment_methods_for_mandate, - &payment_method, - &payment_method_object.payment_method_type, - connector_variant, - ) + .map(|mandate_details| { + let (mandate_type_present, update_mandate_id_present) = + match mandate_details { + data_models::mandates::MandateTypeDetails::MandateType(_) => { + (true, false) + } + data_models::mandates::MandateTypeDetails::MandateDetails( + mand_details, + ) => ( + mand_details.mandate_type.is_some(), + mand_details.update_mandate_id.is_some(), + ), + }; + + if mandate_type_present || update_mandate_id_present { + filter_pm_based_on_supported_payments_for_mandate( + supported_payment_methods_for_mandate, + &payment_method, + &payment_method_object.payment_method_type, + connector_variant, + ) + } else { + true + } }) .unwrap_or(true); diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 381f02659769..378cbb254677 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -712,7 +712,9 @@ impl PaymentCreate { Err(errors::ApiErrorResponse::InvalidRequestData {message:"Only one field out of 'mandate_type' and 'update_mandate_id' was expected, found both".to_string()})? } - let mandate_dets = if let Some(update_id) = request + let mandate_details = if request.mandate_data.is_none() { + None + } else if let Some(update_id) = request .mandate_data .as_ref() .and_then(|inner| inner.update_mandate_id.clone()) @@ -723,8 +725,6 @@ impl PaymentCreate { }; Some(MandateTypeDetails::MandateDetails(mandate_data)) } else { - // let mandate_type: data_models::mandates::MandateDataType = - let mandate_data = MandateDetails { update_mandate_id: None, mandate_type: request @@ -761,7 +761,7 @@ impl PaymentCreate { business_sub_label: request.business_sub_label.clone(), surcharge_amount, tax_amount, - mandate_details: mandate_dets, + mandate_details, ..storage::PaymentAttemptNew::default() }, additional_pm_data, diff --git a/crates/router/src/middleware.rs b/crates/router/src/middleware.rs index 702c3c70e6c6..9205f53a04b7 100644 --- a/crates/router/src/middleware.rs +++ b/crates/router/src/middleware.rs @@ -141,9 +141,14 @@ where let response_fut = self.service.call(req); Box::pin( - response_fut.instrument( + async move { + let response = response_fut.await; + logger::info!(golden_log_line = true); + response + } + .instrument( router_env::tracing::info_span!( - "golden_log_line", + "ROOT_SPAN", payment_id = Empty, merchant_id = Empty, connector_name = Empty, diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs index 012030df6be9..a20d2dd026b9 100644 --- a/crates/router/src/routes/customers.rs +++ b/crates/router/src/routes/customers.rs @@ -147,7 +147,7 @@ pub async fn get_customer_mandates( customer_id: path.into_inner(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -166,6 +166,6 @@ pub async fn get_customer_mandates( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/health.rs b/crates/router/src/routes/health.rs index 89132c3319bf..2183ab07fed7 100644 --- a/crates/router/src/routes/health.rs +++ b/crates/router/src/routes/health.rs @@ -94,6 +94,17 @@ async fn deep_health_check_func(state: app::AppState) -> RouterResponse RouterResponse HttpResponse { let flow = Flow::MandatesList; let payload = payload.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -132,6 +132,6 @@ pub async fn retrieve_mandates_list( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/scheduler/Cargo.toml b/crates/scheduler/Cargo.toml index fe090552edb3..7d4a7821fc94 100644 --- a/crates/scheduler/Cargo.toml +++ b/crates/scheduler/Cargo.toml @@ -13,6 +13,7 @@ kv_store = [] async-trait = "0.1.68" error-stack = "0.3.1" futures = "0.3.28" +num_cpus = "1.15.0" once_cell = "1.18.0" rand = "0.8.5" serde = "1.0.193" diff --git a/crates/scheduler/src/configs/defaults.rs b/crates/scheduler/src/configs/defaults.rs index 25eb19e24f2a..d17c20829ea4 100644 --- a/crates/scheduler/src/configs/defaults.rs +++ b/crates/scheduler/src/configs/defaults.rs @@ -6,6 +6,7 @@ impl Default for super::settings::SchedulerSettings { consumer: super::settings::ConsumerSettings::default(), graceful_shutdown_interval: 60000, loop_interval: 5000, + server: super::settings::Server::default(), } } } @@ -30,3 +31,13 @@ impl Default for super::settings::ConsumerSettings { } } } + +impl Default for super::settings::Server { + fn default() -> Self { + Self { + port: 8080, + workers: num_cpus::get_physical(), + host: "localhost".into(), + } + } +} diff --git a/crates/scheduler/src/configs/settings.rs b/crates/scheduler/src/configs/settings.rs index 56a9f4079ac0..723ef81e70c7 100644 --- a/crates/scheduler/src/configs/settings.rs +++ b/crates/scheduler/src/configs/settings.rs @@ -15,6 +15,15 @@ pub struct SchedulerSettings { pub consumer: ConsumerSettings, pub loop_interval: u64, pub graceful_shutdown_interval: u64, + pub server: Server, +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(default)] +pub struct Server { + pub port: u16, + pub workers: usize, + pub host: String, } #[derive(Debug, Clone, Deserialize)] diff --git a/crates/scheduler/src/configs/validations.rs b/crates/scheduler/src/configs/validations.rs index e9f6621b2a5a..06052f9ff6c7 100644 --- a/crates/scheduler/src/configs/validations.rs +++ b/crates/scheduler/src/configs/validations.rs @@ -19,6 +19,8 @@ impl super::settings::SchedulerSettings { self.producer.validate()?; + self.server.validate()?; + Ok(()) } } @@ -32,3 +34,13 @@ impl super::settings::ProducerSettings { }) } } + +impl super::settings::Server { + pub fn validate(&self) -> Result<(), ApplicationError> { + common_utils::fp_utils::when(self.host.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "server host must not be empty".into(), + )) + }) + } +} diff --git a/postman/collection-json/stripe.postman_collection.json b/postman/collection-json/stripe.postman_collection.json index 847a1323a2c6..fa7650af9e42 100644 --- a/postman/collection-json/stripe.postman_collection.json +++ b/postman/collection-json/stripe.postman_collection.json @@ -6409,7 +6409,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10121,7 +10121,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000009995\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000009995\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -20388,7 +20388,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments",