Bug: Deep health check for outgoing request




diff --git a/crates/api_models/src/health_check.rs b/crates/api_models/src/health_check.rs
index 8323f135134..c1ab2a2d757 100644
--- a/crates/api_models/src/health_check.rs
+++ b/crates/api_models/src/health_check.rs
@@ -5,6 +5,7 @@ pub struct RouterHealthCheckResponse {
     pub locker: bool,
     #[cfg(feature = "olap")]
     pub analytics: bool,
+    pub outgoing_request: bool,
 }
 
 impl common_utils::events::ApiEventMetric for RouterHealthCheckResponse {}
diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs
index 12b688e3d3a..3c3f01dc5f9 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 cbc4290f63b..9052893d4a9 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/health_check.rs b/crates/router/src/core/health_check.rs
index 6fc038b82e9..bc523b4fba6 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/routes/health.rs b/crates/router/src/routes/health.rs
index 89132c3319b..2183ab07fed 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<RouterHe
             })
         })?;
 
+    let outgoing_check = state
+        .health_check_outgoing()
+        .await
+        .map(|_| true)
+        .map_err(|err| {
+            error_stack::report!(errors::ApiErrorResponse::HealthCheckError {
+                component: "Outgoing Request",
+                message: err.to_string()
+            })
+        })?;
+
     logger::debug!("Locker health check end");
 
     let response = RouterHealthCheckResponse {
@@ -102,6 +113,7 @@ async fn deep_health_check_func(state: app::AppState) -> RouterResponse<RouterHe
         locker: locker_status,
         #[cfg(feature = "olap")]
         analytics: analytics_status,
+        outgoing_request: outgoing_check,
     };
 
     Ok(api::ApplicationResponse::Json(response))

## Type of Change
<!-- Put an `x` in the boxes that apply -->
- [x] New feature

## Description
<!-- Describe your changes in detail -->
Closes #3518. Add healthcheck for outgoing calls

## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
Added a check for outgoing requests

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
- `curl --location 'http://localhost:8080/health/ready'`

<img width="907" alt="Screenshot 2024-02-01 at 11 58 57 AM" src="https://github.com/juspay/hyperswitch/assets/43412619/79ebea8f-4ab2-4d35-893d-60974f64b39f">

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code