mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
refactor(redis_interface): separating redis functionality and dependent functionalities outside router crate (#15)
Co-authored-by: Sanchith Hegde
This commit is contained in:
34
Cargo.lock
generated
34
Cargo.lock
generated
@ -830,6 +830,19 @@ dependencies = [
|
|||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "common_utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"error-stack",
|
||||||
|
"masking",
|
||||||
|
"router_env",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "config"
|
name = "config"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
@ -1080,9 +1093,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "error-stack"
|
name = "error-stack"
|
||||||
version = "0.2.1"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c04879c877b85178ad32202703102bcdc1e7c4cb0065b270e55a2c1baff65f2"
|
checksum = "859d224e04b2d93d974c08e375dac9b8d1a513846e44c6666450a57b1ed963f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
@ -2308,6 +2321,21 @@ dependencies = [
|
|||||||
"nom",
|
"nom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redis_interface"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"common_utils",
|
||||||
|
"error-stack",
|
||||||
|
"fred",
|
||||||
|
"router_env",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@ -2431,6 +2459,7 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"bb8",
|
"bb8",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"common_utils",
|
||||||
"config",
|
"config",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"diesel",
|
"diesel",
|
||||||
@ -2448,6 +2477,7 @@ dependencies = [
|
|||||||
"nanoid",
|
"nanoid",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rand",
|
"rand",
|
||||||
|
"redis_interface",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"ring",
|
"ring",
|
||||||
|
|||||||
16
crates/common_utils/Cargo.toml
Normal file
16
crates/common_utils/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "common_utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bytes = "1.2.1"
|
||||||
|
error-stack = "0.2.1"
|
||||||
|
serde = { version = "1.0.145", features = ["derive"] }
|
||||||
|
serde_json = "1.0.85"
|
||||||
|
serde_urlencoded = "0.7.1"
|
||||||
|
|
||||||
|
# First party crates
|
||||||
|
masking = { version = "0.1.0", path = "../masking" }
|
||||||
|
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
|
||||||
11
crates/common_utils/README.md
Normal file
11
crates/common_utils/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Common Utils
|
||||||
|
|
||||||
|
Common functionality required by internal crates
|
||||||
|
|
||||||
|
## Files Tree Layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
└── src : source code
|
||||||
|
└── errors : common error specific types
|
||||||
|
└── ext_traits : traits for extending type functionalities
|
||||||
|
```
|
||||||
40
crates/common_utils/src/errors.rs
Normal file
40
crates/common_utils/src/errors.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//!
|
||||||
|
//! errors and error specific types for universal use
|
||||||
|
|
||||||
|
/// Custom Result
|
||||||
|
/// A custom datatype that wraps the error variant <E> into a report, allowing
|
||||||
|
/// error_stack::Report<E> specific extendability
|
||||||
|
///
|
||||||
|
/// Effectively, equivalent to `Result<T, error_stack::Report<E>>`
|
||||||
|
///
|
||||||
|
pub type CustomResult<T, E> = error_stack::Result<T, E>;
|
||||||
|
|
||||||
|
macro_rules! impl_error_display {
|
||||||
|
($st: ident, $arg: tt) => {
|
||||||
|
impl std::fmt::Display for $st {
|
||||||
|
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
fmt.write_str(&format!(
|
||||||
|
"{{ error_type: {:?}, error_description: {} }}",
|
||||||
|
self, $arg
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_error_type {
|
||||||
|
($name: ident, $arg: tt) => {
|
||||||
|
#[doc = ""]
|
||||||
|
#[doc = stringify!(Error variant $name)]
|
||||||
|
#[doc = stringify!(Custom error variant for $arg)]
|
||||||
|
#[doc = ""]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct $name;
|
||||||
|
|
||||||
|
impl_error_display!($name, $arg);
|
||||||
|
|
||||||
|
impl std::error::Error for $name {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_error_type!(ParsingError, "Parsing error");
|
||||||
279
crates/common_utils/src/ext_traits.rs
Normal file
279
crates/common_utils/src/ext_traits.rs
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
//!
|
||||||
|
//! This module holds traits for extending functionalities for existing datatypes
|
||||||
|
//! & inbuilt datatypes.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use error_stack::{IntoReport, ResultExt};
|
||||||
|
use masking::{ExposeInterface, Secret, Strategy};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::errors::{self, CustomResult};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Encode interface
|
||||||
|
/// An interface for performing type conversions and serialization
|
||||||
|
///
|
||||||
|
pub trait Encode<'e, P>
|
||||||
|
where
|
||||||
|
Self: 'e + std::fmt::Debug,
|
||||||
|
{
|
||||||
|
// If needed get type information/custom error implementation.
|
||||||
|
///
|
||||||
|
/// Converting `Self` into an intermediate representation `<P>`
|
||||||
|
/// and then performing encoding operation using the `Serialize` trait from `serde`
|
||||||
|
/// Specifically to convert into json, by using `serde_json`
|
||||||
|
///
|
||||||
|
fn convert_and_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
|
where
|
||||||
|
P: TryFrom<&'e Self> + Serialize,
|
||||||
|
Result<P, <P as TryFrom<&'e Self>>::Error>: error_stack::ResultExt,
|
||||||
|
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Converting `Self` into an intermediate representation `<P>`
|
||||||
|
/// and then performing encoding operation using the `Serialize` trait from `serde`
|
||||||
|
/// Specifically, to convert into urlencoded, by using `serde_urlencoded`
|
||||||
|
///
|
||||||
|
fn convert_and_url_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
|
where
|
||||||
|
P: TryFrom<&'e Self> + Serialize,
|
||||||
|
Result<P, <P as TryFrom<&'e Self>>::Error>: error_stack::ResultExt,
|
||||||
|
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Functionality, for specifically encoding `Self` into `String`
|
||||||
|
/// after serialization by using `serde::Serialize`
|
||||||
|
///
|
||||||
|
fn encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
|
where
|
||||||
|
Self: Serialize;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Functionality, for specifically encoding `Self` into `String`
|
||||||
|
/// after serialization by using `serde::Serialize`
|
||||||
|
/// specifically, to convert into JSON `String`.
|
||||||
|
///
|
||||||
|
fn encode_to_string_of_json(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
|
where
|
||||||
|
Self: Serialize;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Functionality, for specifically encoding `Self` into `serde_json::Value`
|
||||||
|
/// after serialization by using `serde::Serialize`
|
||||||
|
///
|
||||||
|
fn encode_to_value(&'e self) -> CustomResult<serde_json::Value, errors::ParsingError>
|
||||||
|
where
|
||||||
|
Self: Serialize;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Functionality, for specifically encoding `Self` into `Vec<u8>`
|
||||||
|
/// after serialization by using `serde::Serialize`
|
||||||
|
///
|
||||||
|
fn encode_to_vec(&'e self) -> CustomResult<Vec<u8>, errors::ParsingError>
|
||||||
|
where
|
||||||
|
Self: Serialize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'e, P, A> Encode<'e, P> for A
|
||||||
|
where
|
||||||
|
Self: 'e + std::fmt::Debug,
|
||||||
|
{
|
||||||
|
fn convert_and_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
|
where
|
||||||
|
P: TryFrom<&'e Self> + Serialize,
|
||||||
|
Result<P, <P as TryFrom<&'e Self>>::Error>: error_stack::ResultExt,
|
||||||
|
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize,
|
||||||
|
{
|
||||||
|
serde_json::to_string(&P::try_from(self).change_context(errors::ParsingError)?)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to convert {:?} to a request", self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_and_url_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
|
where
|
||||||
|
P: TryFrom<&'e Self> + Serialize,
|
||||||
|
Result<P, <P as TryFrom<&'e Self>>::Error>: error_stack::ResultExt,
|
||||||
|
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize,
|
||||||
|
{
|
||||||
|
serde_urlencoded::to_string(&P::try_from(self).change_context(errors::ParsingError)?)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to convert {:?} to a request", self))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check without two functions can we combine this
|
||||||
|
fn encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
|
where
|
||||||
|
Self: Serialize,
|
||||||
|
{
|
||||||
|
serde_urlencoded::to_string(self)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to convert {:?} to a request", self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_to_string_of_json(&'e self) -> CustomResult<String, errors::ParsingError>
|
||||||
|
where
|
||||||
|
Self: Serialize,
|
||||||
|
{
|
||||||
|
serde_json::to_string(self)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to convert {:?} to a request", self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_to_value(&'e self) -> CustomResult<serde_json::Value, errors::ParsingError>
|
||||||
|
where
|
||||||
|
Self: Serialize,
|
||||||
|
{
|
||||||
|
serde_json::to_value(self)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to convert {:?} to a value", self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_to_vec(&'e self) -> CustomResult<Vec<u8>, errors::ParsingError>
|
||||||
|
where
|
||||||
|
Self: Serialize,
|
||||||
|
{
|
||||||
|
serde_json::to_vec(self)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to convert {:?} to a value", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Extending functionalities of `bytes::Bytes`
|
||||||
|
///
|
||||||
|
pub trait BytesExt<T> {
|
||||||
|
///
|
||||||
|
/// Convert `bytes::Bytes` into type `<T>` using `serde::Deserialize`
|
||||||
|
///
|
||||||
|
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BytesExt<T> for bytes::Bytes {
|
||||||
|
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
use bytes::Buf;
|
||||||
|
|
||||||
|
serde_json::from_slice::<T>(self.chunk())
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to parse {type_name} from bytes"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Extending functionalities of `[u8]` for performing parsing
|
||||||
|
///
|
||||||
|
pub trait ByteSliceExt<T> {
|
||||||
|
///
|
||||||
|
/// Convert `[u8]` into type `<T>` by using `serde::Deserialize`
|
||||||
|
///
|
||||||
|
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ByteSliceExt<T> for [u8] {
|
||||||
|
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
serde_json::from_slice(self)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to parse {type_name} from &[u8]"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Extending functionalities of `serde_json::Value` for performing parsing
|
||||||
|
///
|
||||||
|
pub trait ValueExt<T> {
|
||||||
|
///
|
||||||
|
/// Convert `serde_json::Value` into type `<T>` by using `serde::Deserialize`
|
||||||
|
///
|
||||||
|
fn parse_value(self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: serde::de::DeserializeOwned;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ValueExt<T> for serde_json::Value {
|
||||||
|
fn parse_value(self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
let debug = format!(
|
||||||
|
"Unable to parse {type_name} from serde_json::Value: {:?}",
|
||||||
|
&self
|
||||||
|
);
|
||||||
|
serde_json::from_value::<T>(self)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| debug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, MaskingStrategy> ValueExt<T> for Secret<serde_json::Value, MaskingStrategy>
|
||||||
|
where
|
||||||
|
MaskingStrategy: Strategy<serde_json::Value>,
|
||||||
|
{
|
||||||
|
fn parse_value(self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
self.expose().parse_value(type_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Extending functionalities of `String` for performing parsing
|
||||||
|
///
|
||||||
|
pub trait StringExt<T> {
|
||||||
|
///
|
||||||
|
/// Convert `String` into type `<T>` (which being an `enum`)
|
||||||
|
///
|
||||||
|
fn parse_enum(self, enum_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: std::str::FromStr,
|
||||||
|
// Requirement for converting the `Err` variant of `FromStr` to `Report<Err>`
|
||||||
|
<T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Convert `serde_json::Value` into type `<T>` by using `serde::Deserialize`
|
||||||
|
///
|
||||||
|
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> StringExt<T> for String {
|
||||||
|
fn parse_enum(self, enum_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: std::str::FromStr,
|
||||||
|
<T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
T::from_str(&self)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Invalid enum variant {self:?} for enum {enum_name}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
serde_json::from_str::<T>(self)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::ParsingError)
|
||||||
|
.attach_printable_lazy(|| format!("Unable to parse {type_name} from string"))
|
||||||
|
}
|
||||||
|
}
|
||||||
17
crates/common_utils/src/lib.rs
Normal file
17
crates/common_utils/src/lib.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#![warn(
|
||||||
|
missing_docs,
|
||||||
|
rust_2018_idioms,
|
||||||
|
missing_debug_implementations,
|
||||||
|
clippy::expect_used,
|
||||||
|
clippy::missing_panics_doc,
|
||||||
|
clippy::panic,
|
||||||
|
clippy::panic_in_result_fn,
|
||||||
|
clippy::panicking_unwrap,
|
||||||
|
clippy::unreachable,
|
||||||
|
clippy::unwrap_in_result,
|
||||||
|
clippy::unwrap_used
|
||||||
|
)]
|
||||||
|
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR" ), "/", "README.md"))]
|
||||||
|
|
||||||
|
pub mod errors;
|
||||||
|
pub mod ext_traits;
|
||||||
18
crates/redis_interface/Cargo.toml
Normal file
18
crates/redis_interface/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "redis_interface"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bytes = "1.2.1"
|
||||||
|
error-stack = "0.2.1"
|
||||||
|
fred = { version = "5.2.0", features = ["metrics", "partial-tracing"] }
|
||||||
|
serde = { version = "1.0.145", features = ["derive"] }
|
||||||
|
serde_json = "1.0.85"
|
||||||
|
serde_urlencoded = "0.7.1"
|
||||||
|
thiserror = "1.0.37"
|
||||||
|
|
||||||
|
# First party crates
|
||||||
|
common_utils = { version = "0.1.0", path = "../common_utils" }
|
||||||
|
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
|
||||||
@ -1,5 +1,13 @@
|
|||||||
|
//!
|
||||||
|
//! An interface to abstract the `fred` commands
|
||||||
|
//!
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use common_utils::{
|
||||||
|
errors::CustomResult,
|
||||||
|
ext_traits::{ByteSliceExt, Encode},
|
||||||
|
};
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use fred::{
|
use fred::{
|
||||||
interfaces::{KeysInterface, StreamsInterface},
|
interfaces::{KeysInterface, StreamsInterface},
|
||||||
@ -11,9 +19,8 @@ use fred::{
|
|||||||
use router_env::{tracing, tracing::instrument};
|
use router_env::{tracing, tracing::instrument};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::errors::{self, CustomResult},
|
errors,
|
||||||
services::redis::types::{RedisEntryId, SetNXReply},
|
types::{RedisEntryId, SetNXReply},
|
||||||
utils::{ByteSliceExt, Encode},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl super::RedisConnectionPool {
|
impl super::RedisConnectionPool {
|
||||||
39
crates/redis_interface/src/errors.rs
Normal file
39
crates/redis_interface/src/errors.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//!
|
||||||
|
//! Errors specific to this custom redis interface
|
||||||
|
//!
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum RedisError {
|
||||||
|
#[error("Failed to set key value in Redis")]
|
||||||
|
SetFailed,
|
||||||
|
#[error("Failed to set key value with expiry in Redis")]
|
||||||
|
SetExFailed,
|
||||||
|
#[error("Failed to set expiry for key value in Redis")]
|
||||||
|
SetExpiryFailed,
|
||||||
|
#[error("Failed to get key value in Redis")]
|
||||||
|
GetFailed,
|
||||||
|
#[error("Failed to delete key value in Redis")]
|
||||||
|
DeleteFailed,
|
||||||
|
#[error("Failed to append entry to Redis stream")]
|
||||||
|
StreamAppendFailed,
|
||||||
|
#[error("Failed to read entries from Redis stream")]
|
||||||
|
StreamReadFailed,
|
||||||
|
#[error("Failed to delete entries from Redis stream")]
|
||||||
|
StreamDeleteFailed,
|
||||||
|
#[error("Failed to acknowledge Redis stream entry")]
|
||||||
|
StreamAcknowledgeFailed,
|
||||||
|
#[error("Failed to create Redis consumer group")]
|
||||||
|
ConsumerGroupCreateFailed,
|
||||||
|
#[error("Failed to destroy Redis consumer group")]
|
||||||
|
ConsumerGroupDestroyFailed,
|
||||||
|
#[error("Failed to delete consumer from consumer group")]
|
||||||
|
ConsumerGroupRemoveConsumerFailed,
|
||||||
|
#[error("Failed to set last ID on consumer group")]
|
||||||
|
ConsumerGroupSetIdFailed,
|
||||||
|
#[error("Failed to set Redis stream message owner")]
|
||||||
|
ConsumerGroupClaimFailed,
|
||||||
|
#[error("Failed to serialize application type to JSON")]
|
||||||
|
JsonSerializationFailed,
|
||||||
|
#[error("Failed to deserialize application type from JSON")]
|
||||||
|
JsonDeserializationFailed,
|
||||||
|
}
|
||||||
@ -1,8 +1,12 @@
|
|||||||
|
// TODO: Add crate & modules documentation for this crate
|
||||||
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
|
pub mod errors;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
use router_env::logger;
|
||||||
|
|
||||||
pub use self::{commands::*, types::*};
|
pub use self::{commands::*, types::*};
|
||||||
use crate::logger;
|
|
||||||
|
|
||||||
pub struct RedisConnectionPool {
|
pub struct RedisConnectionPool {
|
||||||
pub pool: fred::pool::RedisPool,
|
pub pool: fred::pool::RedisPool,
|
||||||
@ -17,7 +21,7 @@ impl RedisConnectionPool {
|
|||||||
///
|
///
|
||||||
/// Panics if a connection to Redis is not successful.
|
/// Panics if a connection to Redis is not successful.
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
pub(crate) async fn new(conf: &crate::configs::settings::Redis) -> Self {
|
pub async fn new(conf: &types::RedisSettings) -> Self {
|
||||||
let redis_connection_url = match conf.cluster_enabled {
|
let redis_connection_url = match conf.cluster_enabled {
|
||||||
// Fred relies on this format for specifying cluster where the host port is ignored & only query parameters are used for node addresses
|
// Fred relies on this format for specifying cluster where the host port is ignored & only query parameters are used for node addresses
|
||||||
// redis-cluster://username:password@host:port?node=bar.com:30002&node=baz.com:30003
|
// redis-cluster://username:password@host:port?node=bar.com:30002&node=baz.com:30003
|
||||||
@ -79,11 +83,23 @@ struct RedisConfig {
|
|||||||
default_stream_read_count: u64,
|
default_stream_read_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&crate::configs::settings::Redis> for RedisConfig {
|
impl From<&types::RedisSettings> for RedisConfig {
|
||||||
fn from(config: &crate::configs::settings::Redis) -> Self {
|
fn from(config: &types::RedisSettings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
default_ttl: config.default_ttl,
|
default_ttl: config.default_ttl,
|
||||||
default_stream_read_count: config.stream_read_count,
|
default_stream_read_count: config.stream_read_count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_redis_error() {
|
||||||
|
let x = errors::RedisError::ConsumerGroupClaimFailed.to_string();
|
||||||
|
|
||||||
|
assert_eq!(x, "Failed to set redis stream message owner".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,23 @@
|
|||||||
|
//!
|
||||||
|
//! Data types and type conversions
|
||||||
|
//! from `fred`'s internal data-types to custom data-types
|
||||||
|
//!
|
||||||
|
#[derive(Debug, serde::Deserialize, Clone)]
|
||||||
|
pub struct RedisSettings {
|
||||||
|
pub host: String,
|
||||||
|
pub port: u16,
|
||||||
|
pub cluster_urls: Vec<String>,
|
||||||
|
pub cluster_enabled: bool,
|
||||||
|
pub use_legacy_version: bool,
|
||||||
|
pub pool_size: usize,
|
||||||
|
pub reconnect_max_attempts: u32,
|
||||||
|
/// Reconnect delay in milliseconds
|
||||||
|
pub reconnect_delay: u32,
|
||||||
|
/// TTL in seconds
|
||||||
|
pub default_ttl: u32,
|
||||||
|
pub stream_read_count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RedisEntryId {
|
pub enum RedisEntryId {
|
||||||
UserSpecifiedID {
|
UserSpecifiedID {
|
||||||
@ -16,7 +16,7 @@ stripe = ["dep:serde_qs"]
|
|||||||
sandbox = ["kms", "stripe"]
|
sandbox = ["kms", "stripe"]
|
||||||
olap = []
|
olap = []
|
||||||
production = []
|
production = []
|
||||||
kv_store = []
|
kv_store = ["dep:fred"]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -37,7 +37,7 @@ diesel = { git = "https://github.com/juspay/diesel", features = ["postgres", "se
|
|||||||
dyn-clone = "1.0.9"
|
dyn-clone = "1.0.9"
|
||||||
encoding_rs = "0.8.31"
|
encoding_rs = "0.8.31"
|
||||||
error-stack = "0.2.1"
|
error-stack = "0.2.1"
|
||||||
fred = { version = "5.2.0", features = ["metrics", "partial-tracing"] }
|
fred = { version = "5.2.0", features = ["metrics", "partial-tracing"] , optional = true }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
http = "0.2.8"
|
http = "0.2.8"
|
||||||
literally = "0.1.3"
|
literally = "0.1.3"
|
||||||
@ -64,7 +64,9 @@ uuid = { version = "1.1.2", features = ["serde", "v4"] }
|
|||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
|
||||||
# First party crates
|
# First party crates
|
||||||
|
common_utils = { version = "0.1.0", path = "../common_utils" }
|
||||||
masking = { version = "0.1.0", path = "../masking" }
|
masking = { version = "0.1.0", path = "../masking" }
|
||||||
|
redis_interface = { version = "0.1.0", path = "../redis_interface" }
|
||||||
router_derive = { version = "0.1.0", path = "../router_derive" }
|
router_derive = { version = "0.1.0", path = "../router_derive" }
|
||||||
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
|
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use config::{Environment, File, FileFormat};
|
use config::{Environment, File, FileFormat};
|
||||||
|
use redis_interface::RedisSettings;
|
||||||
pub use router_env::config::{Log, LogConsole, LogFile, LogTelemetry};
|
pub use router_env::config::{Log, LogConsole, LogFile, LogTelemetry};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
@ -27,7 +28,7 @@ pub struct Settings {
|
|||||||
pub master_database: Database,
|
pub master_database: Database,
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
pub replica_database: Database,
|
pub replica_database: Database,
|
||||||
pub redis: Redis,
|
pub redis: RedisSettings,
|
||||||
pub log: Log,
|
pub log: Log,
|
||||||
pub keys: Keys,
|
pub keys: Keys,
|
||||||
pub locker: Locker,
|
pub locker: Locker,
|
||||||
@ -76,22 +77,6 @@ pub struct Database {
|
|||||||
pub pool_size: u32,
|
pub pool_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub struct Redis {
|
|
||||||
pub host: String,
|
|
||||||
pub port: u16,
|
|
||||||
pub cluster_urls: Vec<String>,
|
|
||||||
pub cluster_enabled: bool,
|
|
||||||
pub use_legacy_version: bool,
|
|
||||||
pub pool_size: usize,
|
|
||||||
pub reconnect_max_attempts: u32,
|
|
||||||
/// Reconnect delay in milliseconds
|
|
||||||
pub reconnect_delay: u32,
|
|
||||||
/// TTL in seconds
|
|
||||||
pub default_ttl: u32,
|
|
||||||
pub stream_read_count: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct Connectors {
|
pub struct Connectors {
|
||||||
pub aci: ConnectorParams,
|
pub aci: ConnectorParams,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use crate::configs::settings::{Database, Settings};
|
|||||||
|
|
||||||
pub type PgPool = bb8::Pool<async_bb8_diesel::ConnectionManager<PgConnection>>;
|
pub type PgPool = bb8::Pool<async_bb8_diesel::ConnectionManager<PgConnection>>;
|
||||||
pub type PgPooledConn = async_bb8_diesel::Connection<PgConnection>;
|
pub type PgPooledConn = async_bb8_diesel::Connection<PgConnection>;
|
||||||
pub type RedisPool = std::sync::Arc<crate::services::redis::RedisConnectionPool>;
|
pub type RedisPool = std::sync::Arc<redis_interface::RedisConnectionPool>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct TestTransaction;
|
struct TestTransaction;
|
||||||
@ -25,8 +25,8 @@ impl CustomizeConnection<PgPooledConn, ConnectionError> for TestTransaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn redis_connection(conf: &Settings) -> crate::services::redis::RedisConnectionPool {
|
pub async fn redis_connection(conf: &Settings) -> redis_interface::RedisConnectionPool {
|
||||||
crate::services::redis::RedisConnectionPool::new(&conf.redis).await
|
redis_interface::RedisConnectionPool::new(&conf.redis).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
|
|||||||
@ -5,15 +5,15 @@ pub(crate) mod utils;
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use actix_web::{body::BoxBody, http::StatusCode, HttpResponse, ResponseError};
|
use actix_web::{body::BoxBody, http::StatusCode, HttpResponse, ResponseError};
|
||||||
|
pub use common_utils::errors::{CustomResult, ParsingError};
|
||||||
use config::ConfigError;
|
use config::ConfigError;
|
||||||
use error_stack;
|
use error_stack;
|
||||||
|
pub use redis_interface::errors::RedisError;
|
||||||
use router_env::opentelemetry::metrics::MetricsError;
|
use router_env::opentelemetry::metrics::MetricsError;
|
||||||
|
|
||||||
pub use self::api_error_response::ApiErrorResponse;
|
pub use self::api_error_response::ApiErrorResponse;
|
||||||
pub(crate) use self::utils::{ApiClientErrorExt, ConnectorErrorExt, StorageErrorExt};
|
pub(crate) use self::utils::{ApiClientErrorExt, ConnectorErrorExt, StorageErrorExt};
|
||||||
use crate::services;
|
use crate::services;
|
||||||
|
|
||||||
pub type CustomResult<T, E> = error_stack::Result<T, E>;
|
|
||||||
pub type RouterResult<T> = CustomResult<T, ApiErrorResponse>;
|
pub type RouterResult<T> = CustomResult<T, ApiErrorResponse>;
|
||||||
pub type RouterResponse<T> = CustomResult<services::BachResponse<T>, ApiErrorResponse>;
|
pub type RouterResponse<T> = CustomResult<services::BachResponse<T>, ApiErrorResponse>;
|
||||||
|
|
||||||
@ -97,7 +97,6 @@ pub enum DatabaseError {
|
|||||||
impl_error_type!(AuthenticationError, "Authentication error");
|
impl_error_type!(AuthenticationError, "Authentication error");
|
||||||
impl_error_type!(AuthorisationError, "Authorisation error");
|
impl_error_type!(AuthorisationError, "Authorisation error");
|
||||||
impl_error_type!(EncryptionError, "Encryption error");
|
impl_error_type!(EncryptionError, "Encryption error");
|
||||||
impl_error_type!(ParsingError, "Parsing error");
|
|
||||||
impl_error_type!(UnexpectedError, "Unexpected error");
|
impl_error_type!(UnexpectedError, "Unexpected error");
|
||||||
impl_error_type!(ValidateError, "validation failed");
|
impl_error_type!(ValidateError, "validation failed");
|
||||||
|
|
||||||
@ -418,42 +417,6 @@ error_to_process_tracker_error!(
|
|||||||
ProcessTrackerError::EValidationError(error_stack::Report<ValidationError>)
|
ProcessTrackerError::EValidationError(error_stack::Report<ValidationError>)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum RedisError {
|
|
||||||
#[error("Failed to set key value in Redis")]
|
|
||||||
SetFailed,
|
|
||||||
#[error("Failed to set key value with expiry in Redis")]
|
|
||||||
SetExFailed,
|
|
||||||
#[error("Failed to set expiry for key value in Redis")]
|
|
||||||
SetExpiryFailed,
|
|
||||||
#[error("Failed to get key value in Redis")]
|
|
||||||
GetFailed,
|
|
||||||
#[error("Failed to delete key value in Redis")]
|
|
||||||
DeleteFailed,
|
|
||||||
#[error("Failed to append entry to redis stream")]
|
|
||||||
StreamAppendFailed,
|
|
||||||
#[error("Failed to read entries from redis stream")]
|
|
||||||
StreamReadFailed,
|
|
||||||
#[error("Failed to delete entries from redis stream")]
|
|
||||||
StreamDeleteFailed,
|
|
||||||
#[error("Failed to acknowledge redis stream entry")]
|
|
||||||
StreamAcknowledgeFailed,
|
|
||||||
#[error("Failed to create redis consumer group")]
|
|
||||||
ConsumerGroupCreateFailed,
|
|
||||||
#[error("Failed to destroy redis consumer group")]
|
|
||||||
ConsumerGroupDestroyFailed,
|
|
||||||
#[error("Failed to delete consumer from consumer group")]
|
|
||||||
ConsumerGroupRemoveConsumerFailed,
|
|
||||||
#[error("Failed to set last ID on consumer group")]
|
|
||||||
ConsumerGroupSetIdFailed,
|
|
||||||
#[error("Failed to set redis stream message owner")]
|
|
||||||
ConsumerGroupClaimFailed,
|
|
||||||
#[error("Failed to serialize application type to json")]
|
|
||||||
JsonSerializationFailed,
|
|
||||||
#[error("Failed to deserialize application type from json")]
|
|
||||||
JsonDeserializationFailed,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum ValidationError {
|
pub enum ValidationError {
|
||||||
#[error("Missing required field: {field_name}")]
|
#[error("Missing required field: {field_name}")]
|
||||||
|
|||||||
@ -148,12 +148,13 @@ mod storage {
|
|||||||
mod storage {
|
mod storage {
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use fred::prelude::*;
|
use fred::prelude::*;
|
||||||
|
use redis_interface::RedisEntryId;
|
||||||
|
|
||||||
use super::IPaymentAttempt;
|
use super::IPaymentAttempt;
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::pg_connection,
|
connection::pg_connection,
|
||||||
core::errors::{self, CustomResult},
|
core::errors::{self, CustomResult},
|
||||||
services::{redis::RedisEntryId, Store},
|
services::Store,
|
||||||
types::storage::{enums, payment_attempt::*},
|
types::storage::{enums, payment_attempt::*},
|
||||||
utils::{date_time, storage_partitioning::KvStorePartition},
|
utils::{date_time, storage_partitioning::KvStorePartition},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -36,12 +36,13 @@ pub trait IPaymentIntent {
|
|||||||
mod storage {
|
mod storage {
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use fred::prelude::{RedisErrorKind, *};
|
use fred::prelude::{RedisErrorKind, *};
|
||||||
|
use redis_interface::RedisEntryId;
|
||||||
|
|
||||||
use super::IPaymentIntent;
|
use super::IPaymentIntent;
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::pg_connection,
|
connection::pg_connection,
|
||||||
core::errors::{self, CustomResult},
|
core::errors::{self, CustomResult},
|
||||||
services::{redis::RedisEntryId, Store},
|
services::Store,
|
||||||
types::{api, storage::payment_intent::*},
|
types::{api, storage::payment_intent::*},
|
||||||
utils::{date_time, storage_partitioning::KvStorePartition},
|
utils::{date_time, storage_partitioning::KvStorePartition},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,7 +22,6 @@ use crate::{
|
|||||||
logger::{error, info},
|
logger::{error, info},
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
scheduler::utils as pt_utils,
|
scheduler::utils as pt_utils,
|
||||||
services::redis::*,
|
|
||||||
types::storage::{self, enums},
|
types::storage::{self, enums},
|
||||||
utils::date_time,
|
utils::date_time,
|
||||||
};
|
};
|
||||||
@ -93,7 +92,11 @@ pub async fn consumer_operations(
|
|||||||
.store
|
.store
|
||||||
.redis_conn
|
.redis_conn
|
||||||
.clone()
|
.clone()
|
||||||
.consumer_group_create(&stream_name, &group_name, &RedisEntryId::AfterLastID)
|
.consumer_group_create(
|
||||||
|
&stream_name,
|
||||||
|
&group_name,
|
||||||
|
&redis_interface::RedisEntryId::AfterLastID,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
if group_created.is_err() {
|
if group_created.is_err() {
|
||||||
info!("Consumer group already exists");
|
info!("Consumer group already exists");
|
||||||
@ -132,7 +135,7 @@ pub async fn consumer_operations(
|
|||||||
#[instrument(skip(db, redis_conn))]
|
#[instrument(skip(db, redis_conn))]
|
||||||
pub async fn fetch_consumer_tasks(
|
pub async fn fetch_consumer_tasks(
|
||||||
db: &dyn Db,
|
db: &dyn Db,
|
||||||
redis_conn: &RedisConnectionPool,
|
redis_conn: &redis_interface::RedisConnectionPool,
|
||||||
stream_name: &str,
|
stream_name: &str,
|
||||||
group_name: &str,
|
group_name: &str,
|
||||||
consumer_name: &str,
|
consumer_name: &str,
|
||||||
|
|||||||
@ -15,7 +15,6 @@ use crate::{
|
|||||||
logger,
|
logger,
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
scheduler::{ProcessTrackerBatch, SchedulerFlow},
|
scheduler::{ProcessTrackerBatch, SchedulerFlow},
|
||||||
services::redis::*,
|
|
||||||
types::storage::{self, enums::ProcessTrackerStatus},
|
types::storage::{self, enums::ProcessTrackerStatus},
|
||||||
utils::{self, date_time, OptionExt, StringExt},
|
utils::{self, date_time, OptionExt, StringExt},
|
||||||
};
|
};
|
||||||
@ -30,7 +29,7 @@ pub async fn acquire_pt_lock(
|
|||||||
let conn = state.store.redis_conn.clone();
|
let conn = state.store.redis_conn.clone();
|
||||||
let is_lock_acquired = conn.set_key_if_not_exist(lock_key, lock_val).await;
|
let is_lock_acquired = conn.set_key_if_not_exist(lock_key, lock_val).await;
|
||||||
match is_lock_acquired {
|
match is_lock_acquired {
|
||||||
Ok(SetNXReply::KeySet) => match conn.set_expiry(lock_key, ttl).await {
|
Ok(redis_interface::SetNXReply::KeySet) => match conn.set_expiry(lock_key, ttl).await {
|
||||||
Ok(()) => true,
|
Ok(()) => true,
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
@ -40,7 +39,7 @@ pub async fn acquire_pt_lock(
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Ok(SetNXReply::KeyNotSet) => {
|
Ok(redis_interface::SetNXReply::KeyNotSet) => {
|
||||||
logger::error!(%tag, "Lock not acquired, previous fetch still in progress");
|
logger::error!(%tag, "Lock not acquired, previous fetch still in progress");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -51,7 +50,11 @@ pub async fn acquire_pt_lock(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn release_pt_lock(redis_conn: &RedisConnectionPool, tag: &str, lock_key: &str) -> bool {
|
pub async fn release_pt_lock(
|
||||||
|
redis_conn: &redis_interface::RedisConnectionPool,
|
||||||
|
tag: &str,
|
||||||
|
lock_key: &str,
|
||||||
|
) -> bool {
|
||||||
let is_lock_released = redis_conn.delete_key(lock_key).await;
|
let is_lock_released = redis_conn.delete_key(lock_key).await;
|
||||||
match is_lock_released {
|
match is_lock_released {
|
||||||
Ok(()) => true,
|
Ok(()) => true,
|
||||||
@ -143,7 +146,7 @@ pub async fn update_status_and_append(
|
|||||||
redis_conn
|
redis_conn
|
||||||
.stream_append_entry(
|
.stream_append_entry(
|
||||||
&pt_batch.stream_name,
|
&pt_batch.stream_name,
|
||||||
&RedisEntryId::AutoGeneratedID,
|
&redis_interface::RedisEntryId::AutoGeneratedID,
|
||||||
field_value_pairs,
|
field_value_pairs,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -186,7 +189,7 @@ pub fn divide_into_batches(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_batches(
|
pub async fn get_batches(
|
||||||
conn: &RedisConnectionPool,
|
conn: &redis_interface::RedisConnectionPool,
|
||||||
stream_name: &str,
|
stream_name: &str,
|
||||||
group_name: &str,
|
group_name: &str,
|
||||||
consumer_name: &str,
|
consumer_name: &str,
|
||||||
@ -194,7 +197,7 @@ pub async fn get_batches(
|
|||||||
let response = conn
|
let response = conn
|
||||||
.stream_read_with_options(
|
.stream_read_with_options(
|
||||||
stream_name,
|
stream_name,
|
||||||
RedisEntryId::UndeliveredEntryID,
|
redis_interface::RedisEntryId::UndeliveredEntryID,
|
||||||
// Update logic for collecting to Vec and flattening, if count > 1 is provided
|
// Update logic for collecting to Vec and flattening, if count > 1 is provided
|
||||||
Some(1),
|
Some(1),
|
||||||
None,
|
None,
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
use std::sync;
|
use std::sync;
|
||||||
|
|
||||||
|
use redis_interface as redis;
|
||||||
|
use redis_interface::errors as redis_errors;
|
||||||
|
|
||||||
use super::{PaymentsSyncWorkflow, ProcessTrackerWorkflow};
|
use super::{PaymentsSyncWorkflow, ProcessTrackerWorkflow};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::payments::{self as payment_flows, operations},
|
core::payments::{self as payment_flows, operations},
|
||||||
@ -7,7 +10,6 @@ use crate::{
|
|||||||
errors,
|
errors,
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
scheduler::{consumer, process_data, utils as pt_utils},
|
scheduler::{consumer, process_data, utils as pt_utils},
|
||||||
services::redis,
|
|
||||||
types::{
|
types::{
|
||||||
api,
|
api,
|
||||||
storage::{self, enums},
|
storage::{self, enums},
|
||||||
@ -93,8 +95,10 @@ pub async fn get_sync_process_schedule_time(
|
|||||||
redis: sync::Arc<redis::RedisConnectionPool>,
|
redis: sync::Arc<redis::RedisConnectionPool>,
|
||||||
retry_count: i32,
|
retry_count: i32,
|
||||||
) -> Result<Option<time::PrimitiveDateTime>, errors::ProcessTrackerError> {
|
) -> Result<Option<time::PrimitiveDateTime>, errors::ProcessTrackerError> {
|
||||||
let redis_mapping: errors::CustomResult<process_data::ConnectorPTMapping, errors::RedisError> =
|
let redis_mapping: errors::CustomResult<
|
||||||
redis
|
process_data::ConnectorPTMapping,
|
||||||
|
redis_errors::RedisError,
|
||||||
|
> = redis
|
||||||
.get_and_deserialize_key(&format!("pt_mapping_{}", connector), "ConnectorPTMapping")
|
.get_and_deserialize_key(&format!("pt_mapping_{}", connector), "ConnectorPTMapping")
|
||||||
.await;
|
.await;
|
||||||
let mapping = match redis_mapping {
|
let mapping = match redis_mapping {
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod encryption;
|
pub mod encryption;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
pub mod redis;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -12,7 +11,7 @@ pub struct Store {
|
|||||||
pub master_pool: crate::db::SqlDb,
|
pub master_pool: crate::db::SqlDb,
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
pub replica_pool: crate::db::SqlDb,
|
pub replica_pool: crate::db::SqlDb,
|
||||||
pub redis_conn: Arc<crate::services::redis::RedisConnectionPool>,
|
pub redis_conn: Arc<redis_interface::RedisConnectionPool>,
|
||||||
#[cfg(feature = "kv_store")]
|
#[cfg(feature = "kv_store")]
|
||||||
pub(crate) config: StoreConfig,
|
pub(crate) config: StoreConfig,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
|
pub use common_utils::ext_traits::{ByteSliceExt, BytesExt, Encode, StringExt, ValueExt};
|
||||||
use error_stack::{report, IntoReport, Report, ResultExt};
|
use error_stack::{report, IntoReport, Report, ResultExt};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::errors::{self, ApiErrorResponse, CustomResult, RouterResult, ValidateError},
|
core::errors::{self, ApiErrorResponse, CustomResult, RouterResult, ValidateError},
|
||||||
logger,
|
logger,
|
||||||
pii::{ExposeInterface, Secret, Strategy},
|
|
||||||
types::api::AddressDetails,
|
types::api::AddressDetails,
|
||||||
utils::when,
|
utils::when,
|
||||||
};
|
};
|
||||||
@ -89,217 +88,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait StringExt<T> {
|
|
||||||
fn parse_enum(self, enum_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: std::str::FromStr,
|
|
||||||
// Requirement for converting the `Err` variant of `FromStr` to `Report<Err>`
|
|
||||||
<T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static;
|
|
||||||
|
|
||||||
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> StringExt<T> for String {
|
|
||||||
fn parse_enum(self, enum_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: std::str::FromStr,
|
|
||||||
<T as std::str::FromStr>::Err: std::error::Error + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
T::from_str(&self)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Invalid enum variant {self:?} for enum {enum_name}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>,
|
|
||||||
{
|
|
||||||
serde_json::from_str::<T>(self)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to parse {type_name} from string"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) trait BytesExt<T> {
|
|
||||||
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> BytesExt<T> for bytes::Bytes {
|
|
||||||
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>,
|
|
||||||
{
|
|
||||||
use bytes::Buf;
|
|
||||||
|
|
||||||
serde_json::from_slice::<T>(self.chunk())
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to parse {type_name} from bytes"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) trait ByteSliceExt<T> {
|
|
||||||
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ByteSliceExt<T> for [u8] {
|
|
||||||
fn parse_struct<'de>(&'de self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: Deserialize<'de>,
|
|
||||||
{
|
|
||||||
serde_json::from_slice(self)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to parse {type_name} from &[u8]"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) trait ValueExt<T> {
|
|
||||||
fn parse_value(self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeOwned;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ValueExt<T> for serde_json::Value {
|
|
||||||
fn parse_value(self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeOwned,
|
|
||||||
{
|
|
||||||
let debug = format!(
|
|
||||||
"Unable to parse {type_name} from serde_json::Value: {:?}",
|
|
||||||
&self
|
|
||||||
);
|
|
||||||
serde_json::from_value::<T>(self)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| debug)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, MaskingStrategy> ValueExt<T> for Secret<serde_json::Value, MaskingStrategy>
|
|
||||||
where
|
|
||||||
MaskingStrategy: Strategy<serde_json::Value>,
|
|
||||||
{
|
|
||||||
fn parse_value(self, type_name: &str) -> CustomResult<T, errors::ParsingError>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeOwned,
|
|
||||||
{
|
|
||||||
self.expose().parse_value(type_name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Encode<'e, P>
|
|
||||||
where
|
|
||||||
Self: 'e + std::fmt::Debug,
|
|
||||||
{
|
|
||||||
// If needed get type information/custom error implementation.
|
|
||||||
fn convert_and_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
|
||||||
where
|
|
||||||
P: TryFrom<&'e Self> + Serialize,
|
|
||||||
Result<P, <P as TryFrom<&'e Self>>::Error>: error_stack::ResultExt,
|
|
||||||
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize;
|
|
||||||
|
|
||||||
fn convert_and_url_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
|
||||||
where
|
|
||||||
P: TryFrom<&'e Self> + Serialize,
|
|
||||||
Result<P, <P as TryFrom<&'e Self>>::Error>: error_stack::ResultExt,
|
|
||||||
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize;
|
|
||||||
|
|
||||||
fn encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
|
||||||
where
|
|
||||||
Self: Serialize;
|
|
||||||
|
|
||||||
fn encode_to_string_of_json(&'e self) -> CustomResult<String, errors::ParsingError>
|
|
||||||
where
|
|
||||||
Self: Serialize;
|
|
||||||
|
|
||||||
fn encode_to_value(&'e self) -> CustomResult<serde_json::Value, errors::ParsingError>
|
|
||||||
where
|
|
||||||
Self: Serialize;
|
|
||||||
|
|
||||||
fn encode_to_vec(&'e self) -> CustomResult<Vec<u8>, errors::ParsingError>
|
|
||||||
where
|
|
||||||
Self: Serialize;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'e, P, A> Encode<'e, P> for A
|
|
||||||
where
|
|
||||||
Self: 'e + std::fmt::Debug,
|
|
||||||
{
|
|
||||||
fn convert_and_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
|
||||||
where
|
|
||||||
P: TryFrom<&'e Self> + Serialize,
|
|
||||||
Result<P, <P as TryFrom<&'e Self>>::Error>: error_stack::ResultExt,
|
|
||||||
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize,
|
|
||||||
{
|
|
||||||
serde_json::to_string(&P::try_from(self).change_context(errors::ParsingError)?)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to convert {:?} to a request", self))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_and_url_encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
|
||||||
where
|
|
||||||
P: TryFrom<&'e Self> + Serialize,
|
|
||||||
Result<P, <P as TryFrom<&'e Self>>::Error>: error_stack::ResultExt,
|
|
||||||
<Result<P, <P as TryFrom<&'e Self>>::Error> as ResultExt>::Ok: Serialize,
|
|
||||||
{
|
|
||||||
serde_urlencoded::to_string(&P::try_from(self).change_context(errors::ParsingError)?)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to convert {:?} to a request", self))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check without two functions can we combine this
|
|
||||||
fn encode(&'e self) -> CustomResult<String, errors::ParsingError>
|
|
||||||
where
|
|
||||||
Self: Serialize,
|
|
||||||
{
|
|
||||||
serde_urlencoded::to_string(self)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to convert {:?} to a request", self))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_to_string_of_json(&'e self) -> CustomResult<String, errors::ParsingError>
|
|
||||||
where
|
|
||||||
Self: Serialize,
|
|
||||||
{
|
|
||||||
serde_json::to_string(self)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to convert {:?} to a request", self))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_to_value(&'e self) -> CustomResult<serde_json::Value, errors::ParsingError>
|
|
||||||
where
|
|
||||||
Self: Serialize,
|
|
||||||
{
|
|
||||||
serde_json::to_value(self)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to convert {:?} to a value", self))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn encode_to_vec(&'e self) -> CustomResult<Vec<u8>, errors::ParsingError>
|
|
||||||
where
|
|
||||||
Self: Serialize,
|
|
||||||
{
|
|
||||||
serde_json::to_vec(self)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ParsingError)
|
|
||||||
.attach_printable_lazy(|| format!("Unable to convert {:?} to a value", self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
/// Merge two `serde_json::Value` instances. Will need to be updated to handle merging arrays.
|
/// Merge two `serde_json::Value` instances. Will need to be updated to handle merging arrays.
|
||||||
pub(crate) fn merge_json_values(a: &mut serde_json::Value, b: &serde_json::Value) {
|
pub(crate) fn merge_json_values(a: &mut serde_json::Value, b: &serde_json::Value) {
|
||||||
|
|||||||
Reference in New Issue
Block a user