mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +08:00 
			
		
		
		
	refactor: move Request and RequestBuilder structs to common_utils crate (#2145)
				
					
				
			This commit is contained in:
		
							
								
								
									
										11
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -497,9 +497,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "async-channel" | ||||
| version = "1.8.0" | ||||
| version = "1.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" | ||||
| checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" | ||||
| dependencies = [ | ||||
|  "concurrent-queue", | ||||
|  "event-listener", | ||||
| @ -1464,6 +1464,7 @@ dependencies = [ | ||||
|  "fake", | ||||
|  "futures", | ||||
|  "hex", | ||||
|  "http", | ||||
|  "masking", | ||||
|  "md5", | ||||
|  "nanoid", | ||||
| @ -1473,6 +1474,7 @@ dependencies = [ | ||||
|  "quick-xml", | ||||
|  "rand 0.8.5", | ||||
|  "regex", | ||||
|  "reqwest", | ||||
|  "ring", | ||||
|  "router_env", | ||||
|  "serde", | ||||
| @ -1480,6 +1482,7 @@ dependencies = [ | ||||
|  "serde_urlencoded", | ||||
|  "signal-hook", | ||||
|  "signal-hook-tokio", | ||||
|  "strum 0.24.1", | ||||
|  "test-case", | ||||
|  "thiserror", | ||||
|  "time 0.3.22", | ||||
| @ -2080,9 +2083,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "fake" | ||||
| version = "2.6.1" | ||||
| version = "2.8.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0a44c765350db469b774425ff1c833890b16ceb9612fb5d7c4bbdf4a1b55f876" | ||||
| checksum = "9af7b0c58ac9d03169e27f080616ce9f64004edca3d2ef4147a811c21b23b319" | ||||
| dependencies = [ | ||||
|  "rand 0.8.5", | ||||
|  "unidecode", | ||||
|  | ||||
| @ -19,17 +19,20 @@ diesel = "2.1.0" | ||||
| error-stack = "0.3.1" | ||||
| futures = { version = "0.3.28", optional = true } | ||||
| hex = "0.4.3" | ||||
| http = "0.2.9" | ||||
| md5 = "0.7.0" | ||||
| nanoid = "0.4.0" | ||||
| once_cell = "1.18.0" | ||||
| quick-xml = { version = "0.28.2", features = ["serialize"] } | ||||
| rand = "0.8.5" | ||||
| regex = "1.8.4" | ||||
| reqwest = { version = "0.11.18", features = ["json", "native-tls", "gzip", "multipart"] } | ||||
| ring = { version = "0.16.20", features = ["std"] } | ||||
| serde = { version = "1.0.163", features = ["derive"] } | ||||
| serde_json = "1.0.96" | ||||
| serde_urlencoded = "0.7.1" | ||||
| signal-hook = { version = "0.3.15", optional = true } | ||||
| strum = { version = "0.24.1", features = ["derive"] } | ||||
| thiserror = "1.0.40" | ||||
| time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } | ||||
| tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"], optional = true } | ||||
|  | ||||
| @ -9,6 +9,8 @@ pub mod errors; | ||||
| pub mod ext_traits; | ||||
| pub mod fp_utils; | ||||
| pub mod pii; | ||||
| #[allow(missing_docs)] // Todo: add docs | ||||
| pub mod request; | ||||
| #[cfg(feature = "signals")] | ||||
| pub mod signals; | ||||
| pub mod validation; | ||||
|  | ||||
							
								
								
									
										206
									
								
								crates/common_utils/src/request.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								crates/common_utils/src/request.rs
									
									
									
									
									
										Normal 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 | ||||
|     } | ||||
| } | ||||
| @ -14,14 +14,14 @@ use actix_web::{body, HttpRequest, HttpResponse, Responder, ResponseError}; | ||||
| use api_models::enums::CaptureMethod; | ||||
| pub use client::{proxy_bypass_urls, ApiClient, MockApiClient, ProxyClient}; | ||||
| use common_utils::errors::ReportSwitchExt; | ||||
| pub use common_utils::request::{ContentType, Method, Request, RequestBuilder}; | ||||
| use error_stack::{report, IntoReport, Report, ResultExt}; | ||||
| use masking::{ExposeOptionInterface, PeekInterface}; | ||||
| use router_env::{instrument, tracing, Tag}; | ||||
| use serde::Serialize; | ||||
| use serde_json::json; | ||||
|  | ||||
| use self::request::{ContentType, HeaderExt, RequestBuilderExt}; | ||||
| pub use self::request::{Method, Request, RequestBuilder}; | ||||
| use self::request::{HeaderExt, RequestBuilderExt}; | ||||
| use crate::{ | ||||
|     configs::settings::{Connectors, Settings}, | ||||
|     consts, | ||||
|  | ||||
| @ -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 masking::Secret; | ||||
| pub use masking::{Mask, Maskable}; | ||||
| use router_env::{instrument, tracing}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use crate::{ | ||||
|     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() | ||||
|     } | ||||
| } | ||||
| use crate::core::errors::{self, CustomResult}; | ||||
|  | ||||
| pub(super) trait HeaderExt { | ||||
|     fn construct_header_map( | ||||
|  | ||||
| @ -17,6 +17,7 @@ pub use api_models::{ | ||||
|     enums::{Connector, PayoutConnectors}, | ||||
|     payouts as payout_types, | ||||
| }; | ||||
| pub use common_utils::request::RequestBody; | ||||
| use common_utils::{pii, pii::Email}; | ||||
| use data_models::mandates::MandateData; | ||||
| 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")] | ||||
| impl<F1, F2> | ||||
|     From<( | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Chethan Rao
					Chethan Rao