mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(logging): Emit a setup error when a restricted keys are used for logging default keys (#5185)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -2002,6 +2002,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"strum 0.26.2",
|
"strum 0.26.2",
|
||||||
|
"thiserror",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6219,6 +6220,7 @@ name = "router_env"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo_metadata 0.18.1",
|
"cargo_metadata 0.18.1",
|
||||||
|
"common_enums",
|
||||||
"config",
|
"config",
|
||||||
"error-stack",
|
"error-stack",
|
||||||
"gethostname",
|
"gethostname",
|
||||||
@ -7238,6 +7240,7 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"bb8",
|
"bb8",
|
||||||
"bytes 1.6.0",
|
"bytes 1.6.0",
|
||||||
|
"common_enums",
|
||||||
"common_utils",
|
"common_utils",
|
||||||
"config",
|
"config",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
@ -8439,9 +8442,9 @@ checksum = "110352d4e9076c67839003c7788d8604e24dcded13e0b375af3efaa8cf468517"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utoipa"
|
name = "utoipa"
|
||||||
|
|||||||
@ -13,6 +13,7 @@ openapi = []
|
|||||||
payouts = []
|
payouts = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
thiserror = "1.0.58"
|
||||||
diesel = { version = "2.1.5", features = ["postgres"] }
|
diesel = { version = "2.1.5", features = ["postgres"] }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.115"
|
||||||
|
|||||||
@ -20,6 +20,80 @@ pub mod diesel_exports {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type ApplicationResult<T> = Result<T, ApplicationError>;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ApplicationError {
|
||||||
|
#[error("Application configuration error")]
|
||||||
|
ConfigurationError,
|
||||||
|
|
||||||
|
#[error("Invalid configuration value provided: {0}")]
|
||||||
|
InvalidConfigurationValueError(String),
|
||||||
|
|
||||||
|
#[error("Metrics error")]
|
||||||
|
MetricsError,
|
||||||
|
|
||||||
|
#[error("I/O: {0}")]
|
||||||
|
IoError(std::io::Error),
|
||||||
|
|
||||||
|
#[error("Error while constructing api client: {0}")]
|
||||||
|
ApiClientError(ApiClientError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, PartialEq, Clone)]
|
||||||
|
pub enum ApiClientError {
|
||||||
|
#[error("Header map construction failed")]
|
||||||
|
HeaderMapConstructionFailed,
|
||||||
|
#[error("Invalid proxy configuration")]
|
||||||
|
InvalidProxyConfiguration,
|
||||||
|
#[error("Client construction failed")]
|
||||||
|
ClientConstructionFailed,
|
||||||
|
#[error("Certificate decode failed")]
|
||||||
|
CertificateDecodeFailed,
|
||||||
|
#[error("Request body serialization failed")]
|
||||||
|
BodySerializationFailed,
|
||||||
|
#[error("Unexpected state reached/Invariants conflicted")]
|
||||||
|
UnexpectedState,
|
||||||
|
|
||||||
|
#[error("URL encoding of request payload failed")]
|
||||||
|
UrlEncodingFailed,
|
||||||
|
#[error("Failed to send request to connector {0}")]
|
||||||
|
RequestNotSent(String),
|
||||||
|
#[error("Failed to decode response")]
|
||||||
|
ResponseDecodingFailed,
|
||||||
|
|
||||||
|
#[error("Server responded with Request Timeout")]
|
||||||
|
RequestTimeoutReceived,
|
||||||
|
|
||||||
|
#[error("connection closed before a message could complete")]
|
||||||
|
ConnectionClosedIncompleteMessage,
|
||||||
|
|
||||||
|
#[error("Server responded with Internal Server Error")]
|
||||||
|
InternalServerErrorReceived,
|
||||||
|
#[error("Server responded with Bad Gateway")]
|
||||||
|
BadGatewayReceived,
|
||||||
|
#[error("Server responded with Service Unavailable")]
|
||||||
|
ServiceUnavailableReceived,
|
||||||
|
#[error("Server responded with Gateway Timeout")]
|
||||||
|
GatewayTimeoutReceived,
|
||||||
|
#[error("Server responded with unexpected response")]
|
||||||
|
UnexpectedServerResponse,
|
||||||
|
}
|
||||||
|
impl ApiClientError {
|
||||||
|
pub fn is_upstream_timeout(&self) -> bool {
|
||||||
|
self == &Self::RequestTimeoutReceived
|
||||||
|
}
|
||||||
|
pub fn is_connection_closed_before_message_could_complete(&self) -> bool {
|
||||||
|
self == &Self::ConnectionClosedIncompleteMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for ApplicationError {
|
||||||
|
fn from(err: std::io::Error) -> Self {
|
||||||
|
Self::IoError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The status of the attempt
|
/// The status of the attempt
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
//! Configs interface
|
//! Configs interface
|
||||||
|
use common_enums::ApplicationError;
|
||||||
use masking::Secret;
|
use masking::Secret;
|
||||||
use router_derive;
|
use router_derive;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use storage_impl::errors::ApplicationError;
|
|
||||||
|
|
||||||
// struct Connectors
|
// struct Connectors
|
||||||
#[allow(missing_docs, missing_debug_implementations)]
|
#[allow(missing_docs, missing_debug_implementations)]
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use error_stack::ResultExt;
|
||||||
use router::{
|
use router::{
|
||||||
configs::settings::{CmdLineConf, Settings},
|
configs::settings::{CmdLineConf, Settings},
|
||||||
core::errors::{ApplicationError, ApplicationResult},
|
core::errors::{ApplicationError, ApplicationResult},
|
||||||
@ -24,7 +25,8 @@ async fn main() -> ApplicationResult<()> {
|
|||||||
&conf.log,
|
&conf.log,
|
||||||
router_env::service_name!(),
|
router_env::service_name!(),
|
||||||
[router_env::service_name!(), "actix_server"],
|
[router_env::service_name!(), "actix_server"],
|
||||||
);
|
)
|
||||||
|
.change_context(ApplicationError::ConfigurationError)?;
|
||||||
|
|
||||||
logger::info!("Application started [{:?}] [{:?}]", conf.server, conf.log);
|
logger::info!("Application started [{:?}] [{:?}]", conf.server, conf.log);
|
||||||
|
|
||||||
@ -39,8 +41,7 @@ async fn main() -> ApplicationResult<()> {
|
|||||||
.expect("Failed to create the server");
|
.expect("Failed to create the server");
|
||||||
let _ = server.await;
|
let _ = server.await;
|
||||||
|
|
||||||
Err(ApplicationError::from(std::io::Error::new(
|
Err(error_stack::Report::from(ApplicationError::from(
|
||||||
std::io::ErrorKind::Other,
|
std::io::Error::new(std::io::ErrorKind::Other, "Server shut down"),
|
||||||
"Server shut down",
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,7 +116,8 @@ pub async fn start_web_server(
|
|||||||
let web_server = actix_web::HttpServer::new(move || {
|
let web_server = actix_web::HttpServer::new(move || {
|
||||||
actix_web::App::new().service(Health::server(state.clone(), service.clone()))
|
actix_web::App::new().service(Health::server(state.clone(), service.clone()))
|
||||||
})
|
})
|
||||||
.bind((server.host.as_str(), server.port))?
|
.bind((server.host.as_str(), server.port))
|
||||||
|
.change_context(ApplicationError::ConfigurationError)?
|
||||||
.workers(server.workers)
|
.workers(server.workers)
|
||||||
.run();
|
.run();
|
||||||
let _ = web_server.handle();
|
let _ = web_server.handle();
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use analytics::{opensearch::OpenSearchConfig, ReportConfig};
|
|||||||
use api_models::{enums, payment_methods::RequiredFieldInfo};
|
use api_models::{enums, payment_methods::RequiredFieldInfo};
|
||||||
use common_utils::ext_traits::ConfigExt;
|
use common_utils::ext_traits::ConfigExt;
|
||||||
use config::{Environment, File};
|
use config::{Environment, File};
|
||||||
|
use error_stack::ResultExt;
|
||||||
#[cfg(feature = "email")]
|
#[cfg(feature = "email")]
|
||||||
use external_services::email::EmailSettings;
|
use external_services::email::EmailSettings;
|
||||||
use external_services::{
|
use external_services::{
|
||||||
@ -33,7 +34,7 @@ use storage_impl::config::QueueStrategy;
|
|||||||
use crate::analytics::AnalyticsConfig;
|
use crate::analytics::AnalyticsConfig;
|
||||||
use crate::{
|
use crate::{
|
||||||
core::errors::{ApplicationError, ApplicationResult},
|
core::errors::{ApplicationError, ApplicationResult},
|
||||||
env::{self, logger, Env},
|
env::{self, Env},
|
||||||
events::EventsConfig,
|
events::EventsConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -722,7 +723,8 @@ impl Settings<SecuredSecret> {
|
|||||||
let environment = env::which();
|
let environment = env::which();
|
||||||
let config_path = router_env::Config::config_path(&environment.to_string(), config_path);
|
let config_path = router_env::Config::config_path(&environment.to_string(), config_path);
|
||||||
|
|
||||||
let config = router_env::Config::builder(&environment.to_string())?
|
let config = router_env::Config::builder(&environment.to_string())
|
||||||
|
.change_context(ApplicationError::ConfigurationError)?
|
||||||
.add_source(File::from(config_path).required(false))
|
.add_source(File::from(config_path).required(false))
|
||||||
.add_source(
|
.add_source(
|
||||||
Environment::with_prefix("ROUTER")
|
Environment::with_prefix("ROUTER")
|
||||||
@ -736,13 +738,12 @@ impl Settings<SecuredSecret> {
|
|||||||
.with_list_parse_key("connector_request_reference_id_config.merchant_ids_send_payment_id_as_connector_request_id"),
|
.with_list_parse_key("connector_request_reference_id_config.merchant_ids_send_payment_id_as_connector_request_id"),
|
||||||
|
|
||||||
)
|
)
|
||||||
.build()?;
|
.build()
|
||||||
|
.change_context(ApplicationError::ConfigurationError)?;
|
||||||
|
|
||||||
serde_path_to_error::deserialize(config).map_err(|error| {
|
serde_path_to_error::deserialize(config)
|
||||||
logger::error!(%error, "Unable to deserialize application configuration");
|
.attach_printable("Unable to deserialize application configuration")
|
||||||
eprintln!("Unable to deserialize application configuration: {error}");
|
.change_context(ApplicationError::ConfigurationError)
|
||||||
ApplicationError::from(error.into_inner())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&self) -> ApplicationResult<()> {
|
pub fn validate(&self) -> ApplicationResult<()> {
|
||||||
@ -756,14 +757,18 @@ impl Settings<SecuredSecret> {
|
|||||||
})?;
|
})?;
|
||||||
if self.log.file.enabled {
|
if self.log.file.enabled {
|
||||||
if self.log.file.file_name.is_default_or_empty() {
|
if self.log.file.file_name.is_default_or_empty() {
|
||||||
return Err(ApplicationError::InvalidConfigurationValueError(
|
return Err(error_stack::Report::from(
|
||||||
"log file name must not be empty".into(),
|
ApplicationError::InvalidConfigurationValueError(
|
||||||
|
"log file name must not be empty".into(),
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.log.file.path.is_default_or_empty() {
|
if self.log.file.path.is_default_or_empty() {
|
||||||
return Err(ApplicationError::InvalidConfigurationValueError(
|
return Err(error_stack::Report::from(
|
||||||
"log directory path must not be empty".into(),
|
ApplicationError::InvalidConfigurationValueError(
|
||||||
|
"log directory path must not be empty".into(),
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ use crate::services;
|
|||||||
pub type RouterResult<T> = CustomResult<T, ApiErrorResponse>;
|
pub type RouterResult<T> = CustomResult<T, ApiErrorResponse>;
|
||||||
pub type RouterResponse<T> = CustomResult<services::ApplicationResponse<T>, ApiErrorResponse>;
|
pub type RouterResponse<T> = CustomResult<services::ApplicationResponse<T>, ApiErrorResponse>;
|
||||||
|
|
||||||
pub type ApplicationResult<T> = Result<T, ApplicationError>;
|
pub type ApplicationResult<T> = error_stack::Result<T, ApplicationError>;
|
||||||
pub type ApplicationResponse<T> = ApplicationResult<services::ApplicationResponse<T>>;
|
pub type ApplicationResponse<T> = ApplicationResult<services::ApplicationResponse<T>>;
|
||||||
|
|
||||||
pub type CustomerResponse<T> =
|
pub type CustomerResponse<T> =
|
||||||
|
|||||||
@ -14,6 +14,7 @@ error-stack = "0.4.1"
|
|||||||
gethostname = "0.4.3"
|
gethostname = "0.4.3"
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
opentelemetry = { version = "0.19.0", features = ["rt-tokio-current-thread", "metrics"] }
|
opentelemetry = { version = "0.19.0", features = ["rt-tokio-current-thread", "metrics"] }
|
||||||
|
common_enums = { path = "../common_enums" }
|
||||||
opentelemetry-otlp = { version = "0.12.0", features = ["metrics"] }
|
opentelemetry-otlp = { version = "0.12.0", features = ["metrics"] }
|
||||||
rustc-hash = "1.1"
|
rustc-hash = "1.1"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use std::{
|
|||||||
io::Write,
|
io::Write,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use config::ConfigError;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::ser::{SerializeMap, Serializer};
|
use serde::ser::{SerializeMap, Serializer};
|
||||||
use serde_json::{ser::Formatter, Value};
|
use serde_json::{ser::Formatter, Value};
|
||||||
@ -155,7 +156,11 @@ where
|
|||||||
/// let formatting_layer = router_env::FormattingLayer::new("my_service", std::io::stdout, serde_json::ser::CompactFormatter);
|
/// let formatting_layer = router_env::FormattingLayer::new("my_service", std::io::stdout, serde_json::ser::CompactFormatter);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
pub fn new(service: &str, dst_writer: W, formatter: F) -> Self {
|
pub fn new(
|
||||||
|
service: &str,
|
||||||
|
dst_writer: W,
|
||||||
|
formatter: F,
|
||||||
|
) -> error_stack::Result<Self, ConfigError> {
|
||||||
Self::new_with_implicit_entries(service, dst_writer, HashMap::new(), formatter)
|
Self::new_with_implicit_entries(service, dst_writer, HashMap::new(), formatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +168,9 @@ where
|
|||||||
pub fn new_with_implicit_entries(
|
pub fn new_with_implicit_entries(
|
||||||
service: &str,
|
service: &str,
|
||||||
dst_writer: W,
|
dst_writer: W,
|
||||||
mut default_fields: HashMap<String, Value>,
|
default_fields: HashMap<String, Value>,
|
||||||
formatter: F,
|
formatter: F,
|
||||||
) -> Self {
|
) -> error_stack::Result<Self, ConfigError> {
|
||||||
let pid = std::process::id();
|
let pid = std::process::id();
|
||||||
let hostname = gethostname::gethostname().to_string_lossy().into_owned();
|
let hostname = gethostname::gethostname().to_string_lossy().into_owned();
|
||||||
let service = service.to_string();
|
let service = service.to_string();
|
||||||
@ -174,20 +179,16 @@ where
|
|||||||
#[cfg(feature = "vergen")]
|
#[cfg(feature = "vergen")]
|
||||||
let build = crate::build!().to_string();
|
let build = crate::build!().to_string();
|
||||||
let env = crate::env::which().to_string();
|
let env = crate::env::which().to_string();
|
||||||
default_fields.retain(|key, value| {
|
for key in default_fields.keys() {
|
||||||
if !IMPLICIT_KEYS.contains(key.as_str()) {
|
if IMPLICIT_KEYS.contains(key.as_str()) {
|
||||||
true
|
return Err(ConfigError::Message(format!(
|
||||||
} else {
|
"A reserved key `{key}` was included in `default_fields` in the log formatting layer"
|
||||||
tracing::warn!(
|
))
|
||||||
?key,
|
.into());
|
||||||
?value,
|
|
||||||
"Attempting to log a reserved entry. It won't be added to the logs"
|
|
||||||
);
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
dst_writer,
|
dst_writer,
|
||||||
pid,
|
pid,
|
||||||
hostname,
|
hostname,
|
||||||
@ -199,7 +200,7 @@ where
|
|||||||
build,
|
build,
|
||||||
default_fields,
|
default_fields,
|
||||||
formatter,
|
formatter,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize common for both span and event entries.
|
/// Serialize common for both span and event entries.
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use ::config::ConfigError;
|
||||||
use opentelemetry::{
|
use opentelemetry::{
|
||||||
global, runtime,
|
global, runtime,
|
||||||
sdk::{
|
sdk::{
|
||||||
@ -36,7 +37,7 @@ pub fn setup(
|
|||||||
config: &config::Log,
|
config: &config::Log,
|
||||||
service_name: &str,
|
service_name: &str,
|
||||||
crates_to_filter: impl AsRef<[&'static str]>,
|
crates_to_filter: impl AsRef<[&'static str]>,
|
||||||
) -> TelemetryGuard {
|
) -> error_stack::Result<TelemetryGuard, ConfigError> {
|
||||||
let mut guards = Vec::new();
|
let mut guards = Vec::new();
|
||||||
|
|
||||||
// Setup OpenTelemetry traces and metrics
|
// Setup OpenTelemetry traces and metrics
|
||||||
@ -69,11 +70,9 @@ pub fn setup(
|
|||||||
&crates_to_filter,
|
&crates_to_filter,
|
||||||
);
|
);
|
||||||
println!("Using file logging filter: {file_filter}");
|
println!("Using file logging filter: {file_filter}");
|
||||||
|
let layer = FormattingLayer::new(service_name, file_writer, CompactFormatter)?
|
||||||
Some(
|
.with_filter(file_filter);
|
||||||
FormattingLayer::new(service_name, file_writer, CompactFormatter)
|
Some(layer)
|
||||||
.with_filter(file_filter),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -107,17 +106,21 @@ pub fn setup(
|
|||||||
}
|
}
|
||||||
config::LogFormat::Json => {
|
config::LogFormat::Json => {
|
||||||
error_stack::Report::set_color_mode(error_stack::fmt::ColorMode::None);
|
error_stack::Report::set_color_mode(error_stack::fmt::ColorMode::None);
|
||||||
let logging_layer =
|
subscriber
|
||||||
FormattingLayer::new(service_name, console_writer, CompactFormatter)
|
.with(
|
||||||
.with_filter(console_filter);
|
FormattingLayer::new(service_name, console_writer, CompactFormatter)?
|
||||||
subscriber.with(logging_layer).init();
|
.with_filter(console_filter),
|
||||||
|
)
|
||||||
|
.init();
|
||||||
}
|
}
|
||||||
config::LogFormat::PrettyJson => {
|
config::LogFormat::PrettyJson => {
|
||||||
error_stack::Report::set_color_mode(error_stack::fmt::ColorMode::None);
|
error_stack::Report::set_color_mode(error_stack::fmt::ColorMode::None);
|
||||||
let logging_layer =
|
subscriber
|
||||||
FormattingLayer::new(service_name, console_writer, PrettyFormatter::new())
|
.with(
|
||||||
.with_filter(console_filter);
|
FormattingLayer::new(service_name, console_writer, PrettyFormatter::new())?
|
||||||
subscriber.with(logging_layer).init();
|
.with_filter(console_filter),
|
||||||
|
)
|
||||||
|
.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -126,10 +129,10 @@ pub fn setup(
|
|||||||
|
|
||||||
// Returning the TelemetryGuard for logs to be printed and metrics to be collected until it is
|
// Returning the TelemetryGuard for logs to be printed and metrics to be collected until it is
|
||||||
// dropped
|
// dropped
|
||||||
TelemetryGuard {
|
Ok(TelemetryGuard {
|
||||||
_log_guards: guards,
|
_log_guards: guards,
|
||||||
_metrics_controller,
|
_metrics_controller,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_opentelemetry_exporter(config: &config::LogTelemetry) -> TonicExporterBuilder {
|
fn get_opentelemetry_exporter(config: &config::LogTelemetry) -> TonicExporterBuilder {
|
||||||
|
|||||||
@ -1,25 +1,25 @@
|
|||||||
#![allow(clippy::unwrap_used)]
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
mod test_module;
|
mod test_module;
|
||||||
|
use ::config::ConfigError;
|
||||||
use router_env::TelemetryGuard;
|
use router_env::TelemetryGuard;
|
||||||
|
|
||||||
use self::test_module::some_module::*;
|
use self::test_module::some_module::*;
|
||||||
|
|
||||||
fn logger() -> &'static TelemetryGuard {
|
fn logger() -> error_stack::Result<&'static TelemetryGuard, ConfigError> {
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
static INSTANCE: OnceCell<TelemetryGuard> = OnceCell::new();
|
static INSTANCE: OnceCell<TelemetryGuard> = OnceCell::new();
|
||||||
INSTANCE.get_or_init(|| {
|
Ok(INSTANCE.get_or_init(|| {
|
||||||
let config = router_env::Config::new().unwrap();
|
let config = router_env::Config::new().unwrap();
|
||||||
|
|
||||||
router_env::setup(&config.log, "router_env_test", [])
|
router_env::setup(&config.log, "router_env_test", []).unwrap()
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn basic() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn basic() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
logger();
|
logger()?;
|
||||||
|
|
||||||
fn_with_colon(13).await;
|
fn_with_colon(13).await;
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@ payouts = ["hyperswitch_domain_models/payouts"]
|
|||||||
# First Party dependencies
|
# First Party dependencies
|
||||||
api_models = { version = "0.1.0", path = "../api_models" }
|
api_models = { version = "0.1.0", path = "../api_models" }
|
||||||
common_utils = { version = "0.1.0", path = "../common_utils" }
|
common_utils = { version = "0.1.0", path = "../common_utils" }
|
||||||
|
common_enums = { version = "0.1.0", path = "../common_enums" }
|
||||||
hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false }
|
hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false }
|
||||||
diesel_models = { version = "0.1.0", path = "../diesel_models" }
|
diesel_models = { version = "0.1.0", path = "../diesel_models" }
|
||||||
masking = { version = "0.1.0", path = "../masking" }
|
masking = { version = "0.1.0", path = "../masking" }
|
||||||
|
|||||||
@ -1,17 +1,10 @@
|
|||||||
use std::fmt::Display;
|
pub use common_enums::{ApiClientError, ApplicationError, ApplicationResult};
|
||||||
|
|
||||||
use actix_web::ResponseError;
|
|
||||||
use common_utils::errors::ErrorSwitch;
|
use common_utils::errors::ErrorSwitch;
|
||||||
use config::ConfigError;
|
|
||||||
use http::StatusCode;
|
|
||||||
use hyperswitch_domain_models::errors::StorageError as DataStorageError;
|
use hyperswitch_domain_models::errors::StorageError as DataStorageError;
|
||||||
pub use redis_interface::errors::RedisError;
|
pub use redis_interface::errors::RedisError;
|
||||||
use router_env::opentelemetry::metrics::MetricsError;
|
|
||||||
|
|
||||||
use crate::store::errors::DatabaseError;
|
use crate::store::errors::DatabaseError;
|
||||||
|
|
||||||
pub type ApplicationResult<T> = Result<T, ApplicationError>;
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum StorageError {
|
pub enum StorageError {
|
||||||
#[error("DatabaseError: {0:?}")]
|
#[error("DatabaseError: {0:?}")]
|
||||||
@ -154,115 +147,6 @@ impl RedisErrorExt for error_stack::Report<RedisError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum ApplicationError {
|
|
||||||
// Display's impl can be overridden by the attribute error marco.
|
|
||||||
// Don't use Debug here, Debug gives error stack in response.
|
|
||||||
#[error("Application configuration error: {0}")]
|
|
||||||
ConfigurationError(ConfigError),
|
|
||||||
|
|
||||||
#[error("Invalid configuration value provided: {0}")]
|
|
||||||
InvalidConfigurationValueError(String),
|
|
||||||
|
|
||||||
#[error("Metrics error: {0}")]
|
|
||||||
MetricsError(MetricsError),
|
|
||||||
|
|
||||||
#[error("I/O: {0}")]
|
|
||||||
IoError(std::io::Error),
|
|
||||||
|
|
||||||
#[error("Error while constructing api client: {0}")]
|
|
||||||
ApiClientError(ApiClientError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<MetricsError> for ApplicationError {
|
|
||||||
fn from(err: MetricsError) -> Self {
|
|
||||||
Self::MetricsError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for ApplicationError {
|
|
||||||
fn from(err: std::io::Error) -> Self {
|
|
||||||
Self::IoError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ConfigError> for ApplicationError {
|
|
||||||
fn from(err: ConfigError) -> Self {
|
|
||||||
Self::ConfigurationError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error_response<T: Display>(err: &T) -> actix_web::HttpResponse {
|
|
||||||
actix_web::HttpResponse::BadRequest()
|
|
||||||
.content_type(mime::APPLICATION_JSON)
|
|
||||||
.body(format!(r#"{{ "error": {{ "message": "{err}" }} }}"#))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResponseError for ApplicationError {
|
|
||||||
fn status_code(&self) -> StatusCode {
|
|
||||||
match self {
|
|
||||||
Self::MetricsError(_)
|
|
||||||
| Self::IoError(_)
|
|
||||||
| Self::ConfigurationError(_)
|
|
||||||
| Self::InvalidConfigurationValueError(_)
|
|
||||||
| Self::ApiClientError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error_response(&self) -> actix_web::HttpResponse {
|
|
||||||
error_response(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, PartialEq, Clone)]
|
|
||||||
pub enum ApiClientError {
|
|
||||||
#[error("Header map construction failed")]
|
|
||||||
HeaderMapConstructionFailed,
|
|
||||||
#[error("Invalid proxy configuration")]
|
|
||||||
InvalidProxyConfiguration,
|
|
||||||
#[error("Client construction failed")]
|
|
||||||
ClientConstructionFailed,
|
|
||||||
#[error("Certificate decode failed")]
|
|
||||||
CertificateDecodeFailed,
|
|
||||||
#[error("Request body serialization failed")]
|
|
||||||
BodySerializationFailed,
|
|
||||||
#[error("Unexpected state reached/Invariants conflicted")]
|
|
||||||
UnexpectedState,
|
|
||||||
|
|
||||||
#[error("URL encoding of request payload failed")]
|
|
||||||
UrlEncodingFailed,
|
|
||||||
#[error("Failed to send request to connector {0}")]
|
|
||||||
RequestNotSent(String),
|
|
||||||
#[error("Failed to decode response")]
|
|
||||||
ResponseDecodingFailed,
|
|
||||||
|
|
||||||
#[error("Server responded with Request Timeout")]
|
|
||||||
RequestTimeoutReceived,
|
|
||||||
|
|
||||||
#[error("connection closed before a message could complete")]
|
|
||||||
ConnectionClosedIncompleteMessage,
|
|
||||||
|
|
||||||
#[error("Server responded with Internal Server Error")]
|
|
||||||
InternalServerErrorReceived,
|
|
||||||
#[error("Server responded with Bad Gateway")]
|
|
||||||
BadGatewayReceived,
|
|
||||||
#[error("Server responded with Service Unavailable")]
|
|
||||||
ServiceUnavailableReceived,
|
|
||||||
#[error("Server responded with Gateway Timeout")]
|
|
||||||
GatewayTimeoutReceived,
|
|
||||||
#[error("Server responded with unexpected response")]
|
|
||||||
UnexpectedServerResponse,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApiClientError {
|
|
||||||
pub fn is_upstream_timeout(&self) -> bool {
|
|
||||||
self == &Self::RequestTimeoutReceived
|
|
||||||
}
|
|
||||||
pub fn is_connection_closed_before_message_could_complete(&self) -> bool {
|
|
||||||
self == &Self::ConnectionClosedIncompleteMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, PartialEq)]
|
#[derive(Debug, thiserror::Error, PartialEq)]
|
||||||
pub enum ConnectorError {
|
pub enum ConnectorError {
|
||||||
#[error("Error while obtaining URL for the integration")]
|
#[error("Error while obtaining URL for the integration")]
|
||||||
|
|||||||
Reference in New Issue
Block a user