refactor: move Request and RequestBuilder structs to common_utils crate (#2145)

This commit is contained in:
Chethan Rao
2023-09-13 16:05:26 +05:30
committed by GitHub
parent a7a3f4f44b
commit 21be67ada0
7 changed files with 227 additions and 213 deletions

11
Cargo.lock generated
View File

@ -497,9 +497,9 @@ dependencies = [
[[package]] [[package]]
name = "async-channel" name = "async-channel"
version = "1.8.0" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
dependencies = [ dependencies = [
"concurrent-queue", "concurrent-queue",
"event-listener", "event-listener",
@ -1464,6 +1464,7 @@ dependencies = [
"fake", "fake",
"futures", "futures",
"hex", "hex",
"http",
"masking", "masking",
"md5", "md5",
"nanoid", "nanoid",
@ -1473,6 +1474,7 @@ dependencies = [
"quick-xml", "quick-xml",
"rand 0.8.5", "rand 0.8.5",
"regex", "regex",
"reqwest",
"ring", "ring",
"router_env", "router_env",
"serde", "serde",
@ -1480,6 +1482,7 @@ dependencies = [
"serde_urlencoded", "serde_urlencoded",
"signal-hook", "signal-hook",
"signal-hook-tokio", "signal-hook-tokio",
"strum 0.24.1",
"test-case", "test-case",
"thiserror", "thiserror",
"time 0.3.22", "time 0.3.22",
@ -2080,9 +2083,9 @@ dependencies = [
[[package]] [[package]]
name = "fake" name = "fake"
version = "2.6.1" version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a44c765350db469b774425ff1c833890b16ceb9612fb5d7c4bbdf4a1b55f876" checksum = "9af7b0c58ac9d03169e27f080616ce9f64004edca3d2ef4147a811c21b23b319"
dependencies = [ dependencies = [
"rand 0.8.5", "rand 0.8.5",
"unidecode", "unidecode",

View File

@ -19,17 +19,20 @@ diesel = "2.1.0"
error-stack = "0.3.1" error-stack = "0.3.1"
futures = { version = "0.3.28", optional = true } futures = { version = "0.3.28", optional = true }
hex = "0.4.3" hex = "0.4.3"
http = "0.2.9"
md5 = "0.7.0" md5 = "0.7.0"
nanoid = "0.4.0" nanoid = "0.4.0"
once_cell = "1.18.0" once_cell = "1.18.0"
quick-xml = { version = "0.28.2", features = ["serialize"] } quick-xml = { version = "0.28.2", features = ["serialize"] }
rand = "0.8.5" rand = "0.8.5"
regex = "1.8.4" regex = "1.8.4"
reqwest = { version = "0.11.18", features = ["json", "native-tls", "gzip", "multipart"] }
ring = { version = "0.16.20", features = ["std"] } ring = { version = "0.16.20", features = ["std"] }
serde = { version = "1.0.163", features = ["derive"] } serde = { version = "1.0.163", features = ["derive"] }
serde_json = "1.0.96" serde_json = "1.0.96"
serde_urlencoded = "0.7.1" serde_urlencoded = "0.7.1"
signal-hook = { version = "0.3.15", optional = true } signal-hook = { version = "0.3.15", optional = true }
strum = { version = "0.24.1", features = ["derive"] }
thiserror = "1.0.40" thiserror = "1.0.40"
time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] }
tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"], optional = true } tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"], optional = true }

View File

@ -9,6 +9,8 @@ pub mod errors;
pub mod ext_traits; pub mod ext_traits;
pub mod fp_utils; pub mod fp_utils;
pub mod pii; pub mod pii;
#[allow(missing_docs)] // Todo: add docs
pub mod request;
#[cfg(feature = "signals")] #[cfg(feature = "signals")]
pub mod signals; pub mod signals;
pub mod validation; pub mod validation;

View File

@ -0,0 +1,206 @@
use masking::{Maskable, Secret};
#[cfg(feature = "logs")]
use router_env::logger;
use serde::{Deserialize, Serialize};
use crate::errors;
pub type Headers = std::collections::HashSet<(String, Maskable<String>)>;
#[derive(
Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, strum::Display, strum::EnumString,
)]
#[serde(rename_all = "UPPERCASE")]
#[strum(serialize_all = "UPPERCASE")]
pub enum Method {
Get,
Post,
Put,
Delete,
}
#[derive(Deserialize, Serialize, Debug)]
pub enum ContentType {
Json,
FormUrlEncoded,
FormData,
}
fn default_request_headers() -> [(String, Maskable<String>); 1] {
use http::header;
[(header::VIA.to_string(), "HyperSwitch".to_string().into())]
}
#[derive(Debug)]
pub struct Request {
pub url: String,
pub headers: Headers,
pub payload: Option<Secret<String>>,
pub method: Method,
pub content_type: Option<ContentType>,
pub certificate: Option<String>,
pub certificate_key: Option<String>,
pub form_data: Option<reqwest::multipart::Form>,
}
impl Request {
pub fn new(method: Method, url: &str) -> Self {
Self {
method,
url: String::from(url),
headers: std::collections::HashSet::new(),
payload: None,
content_type: None,
certificate: None,
certificate_key: None,
form_data: None,
}
}
pub fn set_body(&mut self, body: String) {
self.payload = Some(body.into());
}
pub fn add_default_headers(&mut self) {
self.headers.extend(default_request_headers());
}
pub fn add_header(&mut self, header: &str, value: Maskable<String>) {
self.headers.insert((String::from(header), value));
}
pub fn add_content_type(&mut self, content_type: ContentType) {
self.content_type = Some(content_type);
}
pub fn add_certificate(&mut self, certificate: Option<String>) {
self.certificate = certificate;
}
pub fn add_certificate_key(&mut self, certificate_key: Option<String>) {
self.certificate = certificate_key;
}
pub fn set_form_data(&mut self, form_data: reqwest::multipart::Form) {
self.form_data = Some(form_data);
}
}
#[derive(Debug)]
pub struct RequestBuilder {
pub url: String,
pub headers: Headers,
pub payload: Option<Secret<String>>,
pub method: Method,
pub content_type: Option<ContentType>,
pub certificate: Option<String>,
pub certificate_key: Option<String>,
pub form_data: Option<reqwest::multipart::Form>,
}
impl RequestBuilder {
pub fn new() -> Self {
Self {
method: Method::Get,
url: String::with_capacity(1024),
headers: std::collections::HashSet::new(),
payload: None,
content_type: None,
certificate: None,
certificate_key: None,
form_data: None,
}
}
pub fn url(mut self, url: &str) -> Self {
self.url = url.into();
self
}
pub fn method(mut self, method: Method) -> Self {
self.method = method;
self
}
pub fn attach_default_headers(mut self) -> Self {
self.headers.extend(default_request_headers());
self
}
pub fn header(mut self, header: &str, value: &str) -> Self {
self.headers.insert((header.into(), value.into()));
self
}
pub fn headers(mut self, headers: Vec<(String, Maskable<String>)>) -> Self {
let mut h = headers.into_iter().map(|(h, v)| (h, v));
self.headers.extend(&mut h);
self
}
pub fn form_data(mut self, form_data: Option<reqwest::multipart::Form>) -> Self {
self.form_data = form_data;
self
}
pub fn body(mut self, option_body: Option<RequestBody>) -> Self {
self.payload = option_body.map(RequestBody::get_inner_value);
self
}
pub fn content_type(mut self, content_type: ContentType) -> Self {
self.content_type = Some(content_type);
self
}
pub fn add_certificate(mut self, certificate: Option<String>) -> Self {
self.certificate = certificate;
self
}
pub fn add_certificate_key(mut self, certificate_key: Option<String>) -> Self {
self.certificate_key = certificate_key;
self
}
pub fn build(self) -> Request {
Request {
method: self.method,
url: self.url,
headers: self.headers,
payload: self.payload,
content_type: self.content_type,
certificate: self.certificate,
certificate_key: self.certificate_key,
form_data: self.form_data,
}
}
}
impl Default for RequestBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct RequestBody(Secret<String>);
impl RequestBody {
pub fn log_and_get_request_body<T, F>(
body: T,
encoder: F,
) -> errors::CustomResult<Self, errors::ParsingError>
where
F: FnOnce(T) -> errors::CustomResult<String, errors::ParsingError>,
T: std::fmt::Debug,
{
#[cfg(feature = "logs")]
logger::info!(connector_request_body=?body);
Ok(Self(Secret::new(encoder(body)?)))
}
pub fn get_inner_value(request_body: Self) -> Secret<String> {
request_body.0
}
}

View File

@ -14,14 +14,14 @@ use actix_web::{body, HttpRequest, HttpResponse, Responder, ResponseError};
use api_models::enums::CaptureMethod; use api_models::enums::CaptureMethod;
pub use client::{proxy_bypass_urls, ApiClient, MockApiClient, ProxyClient}; pub use client::{proxy_bypass_urls, ApiClient, MockApiClient, ProxyClient};
use common_utils::errors::ReportSwitchExt; use common_utils::errors::ReportSwitchExt;
pub use common_utils::request::{ContentType, Method, Request, RequestBuilder};
use error_stack::{report, IntoReport, Report, ResultExt}; use error_stack::{report, IntoReport, Report, ResultExt};
use masking::{ExposeOptionInterface, PeekInterface}; use masking::{ExposeOptionInterface, PeekInterface};
use router_env::{instrument, tracing, Tag}; use router_env::{instrument, tracing, Tag};
use serde::Serialize; use serde::Serialize;
use serde_json::json; use serde_json::json;
use self::request::{ContentType, HeaderExt, RequestBuilderExt}; use self::request::{HeaderExt, RequestBuilderExt};
pub use self::request::{Method, Request, RequestBuilder};
use crate::{ use crate::{
configs::settings::{Connectors, Settings}, configs::settings::{Connectors, Settings},
consts, consts,

View File

@ -1,193 +1,12 @@
use std::{collections, str::FromStr}; use std::str::FromStr;
pub use common_utils::request::ContentType;
use common_utils::request::Headers;
use error_stack::{IntoReport, ResultExt}; use error_stack::{IntoReport, ResultExt};
use masking::Secret;
pub use masking::{Mask, Maskable}; pub use masking::{Mask, Maskable};
use router_env::{instrument, tracing}; use router_env::{instrument, tracing};
use serde::{Deserialize, Serialize};
use crate::{ use crate::core::errors::{self, CustomResult};
core::errors::{self, CustomResult},
types,
};
pub(crate) type Headers = collections::HashSet<(String, Maskable<String>)>;
#[derive(
Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, strum::Display, strum::EnumString,
)]
#[serde(rename_all = "UPPERCASE")]
#[strum(serialize_all = "UPPERCASE")]
pub enum Method {
Get,
Post,
Put,
Delete,
}
#[derive(Deserialize, Serialize, Debug)]
pub enum ContentType {
Json,
FormUrlEncoded,
FormData,
}
fn default_request_headers() -> [(String, Maskable<String>); 1] {
use http::header;
[(header::VIA.to_string(), "HyperSwitch".to_string().into())]
}
#[derive(Debug)]
pub struct Request {
pub url: String,
pub headers: Headers,
pub payload: Option<Secret<String>>,
pub method: Method,
pub content_type: Option<ContentType>,
pub certificate: Option<String>,
pub certificate_key: Option<String>,
pub form_data: Option<reqwest::multipart::Form>,
}
impl Request {
pub fn new(method: Method, url: &str) -> Self {
Self {
method,
url: String::from(url),
headers: collections::HashSet::new(),
payload: None,
content_type: None,
certificate: None,
certificate_key: None,
form_data: None,
}
}
pub fn set_body(&mut self, body: String) {
self.payload = Some(body.into());
}
pub fn add_default_headers(&mut self) {
self.headers.extend(default_request_headers());
}
pub fn add_header(&mut self, header: &str, value: Maskable<String>) {
self.headers.insert((String::from(header), value));
}
pub fn add_content_type(&mut self, content_type: ContentType) {
self.content_type = Some(content_type);
}
pub fn add_certificate(&mut self, certificate: Option<String>) {
self.certificate = certificate;
}
pub fn add_certificate_key(&mut self, certificate_key: Option<String>) {
self.certificate = certificate_key;
}
pub fn set_form_data(&mut self, form_data: reqwest::multipart::Form) {
self.form_data = Some(form_data);
}
}
pub struct RequestBuilder {
pub url: String,
pub headers: Headers,
pub payload: Option<Secret<String>>,
pub method: Method,
pub content_type: Option<ContentType>,
pub certificate: Option<String>,
pub certificate_key: Option<String>,
pub form_data: Option<reqwest::multipart::Form>,
}
impl RequestBuilder {
pub fn new() -> Self {
Self {
method: Method::Get,
url: String::with_capacity(1024),
headers: std::collections::HashSet::new(),
payload: None,
content_type: None,
certificate: None,
certificate_key: None,
form_data: None,
}
}
pub fn url(mut self, url: &str) -> Self {
self.url = url.into();
self
}
pub fn method(mut self, method: Method) -> Self {
self.method = method;
self
}
pub fn attach_default_headers(mut self) -> Self {
self.headers.extend(default_request_headers());
self
}
pub fn header(mut self, header: &str, value: &str) -> Self {
self.headers.insert((header.into(), value.into()));
self
}
pub fn headers(mut self, headers: Vec<(String, Maskable<String>)>) -> Self {
let mut h = headers.into_iter().map(|(h, v)| (h, v));
self.headers.extend(&mut h);
self
}
pub fn form_data(mut self, form_data: Option<reqwest::multipart::Form>) -> Self {
self.form_data = form_data;
self
}
pub fn body(mut self, option_body: Option<types::RequestBody>) -> Self {
self.payload = option_body.map(types::RequestBody::get_inner_value);
self
}
pub fn content_type(mut self, content_type: ContentType) -> Self {
self.content_type = Some(content_type);
self
}
pub fn add_certificate(mut self, certificate: Option<String>) -> Self {
self.certificate = certificate;
self
}
pub fn add_certificate_key(mut self, certificate_key: Option<String>) -> Self {
self.certificate_key = certificate_key;
self
}
pub fn build(self) -> Request {
Request {
method: self.method,
url: self.url,
headers: self.headers,
payload: self.payload,
content_type: self.content_type,
certificate: self.certificate,
certificate_key: self.certificate_key,
form_data: self.form_data,
}
}
}
impl Default for RequestBuilder {
fn default() -> Self {
Self::new()
}
}
pub(super) trait HeaderExt { pub(super) trait HeaderExt {
fn construct_header_map( fn construct_header_map(

View File

@ -17,6 +17,7 @@ pub use api_models::{
enums::{Connector, PayoutConnectors}, enums::{Connector, PayoutConnectors},
payouts as payout_types, payouts as payout_types,
}; };
pub use common_utils::request::RequestBody;
use common_utils::{pii, pii::Email}; use common_utils::{pii, pii::Email};
use data_models::mandates::MandateData; use data_models::mandates::MandateData;
use error_stack::{IntoReport, ResultExt}; use error_stack::{IntoReport, ResultExt};
@ -1062,26 +1063,6 @@ impl<F1, F2, T1, T2> From<(&RouterData<F1, T1, PaymentsResponseData>, T2)>
} }
} }
#[derive(Clone, Debug)]
pub struct RequestBody(Secret<String>);
impl RequestBody {
pub fn log_and_get_request_body<T, F>(
body: T,
encoder: F,
) -> errors::CustomResult<Self, errors::ParsingError>
where
F: FnOnce(T) -> errors::CustomResult<String, errors::ParsingError>,
T: std::fmt::Debug,
{
router_env::logger::info!(connector_request_body=?body);
Ok(Self(Secret::new(encoder(body)?)))
}
pub fn get_inner_value(request_body: Self) -> Secret<String> {
request_body.0
}
}
#[cfg(feature = "payouts")] #[cfg(feature = "payouts")]
impl<F1, F2> impl<F1, F2>
From<( From<(