refactor(storage): remove id from payment intent, attempt and remove datamodel ext from payment intent (#4923)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Prajjwal Kumar <prajjwal.kumar@juspay.in>
This commit is contained in:
Narayan Bhat
2024-06-19 17:10:32 +05:30
committed by GitHub
parent 2106a27f40
commit bec51a3557
71 changed files with 1319 additions and 935 deletions

View File

@ -16,11 +16,12 @@ payouts = ["api_models/payouts"]
# First party deps
api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] }
common_enums = { version = "0.1.0", path = "../common_enums" }
common_utils = { version = "0.1.0", path = "../common_utils" }
common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext", "metrics"] }
masking = { version = "0.1.0", path = "../masking" }
diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] }
cards = {version = "0.1.0", path = "../cards"}
router_derive = {version = "0.1.0", path = "../router_derive"}
cards = { version = "0.1.0", path = "../cards" }
router_derive = { version = "0.1.0", path = "../router_derive" }
router_env = { version = "0.1.0", path = "../router_env" }
# Third party deps
actix-web = "4.5.1"
@ -35,4 +36,4 @@ thiserror = "1.0.58"
time = { version = "0.3.35", features = ["serde", "serde-well-known", "std"] }
url = { version = "2.5.0", features = ["serde"] }
utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order", "time"] }
futures = "0.3.30"

View File

@ -0,0 +1,31 @@
use common_utils::errors::{CustomResult, ValidationError};
use masking::Secret;
/// Trait for converting domain types to storage models
#[async_trait::async_trait]
pub trait Conversion {
type DstType;
type NewDstType;
async fn convert(self) -> CustomResult<Self::DstType, ValidationError>;
async fn convert_back(
item: Self::DstType,
key: &Secret<Vec<u8>>,
) -> CustomResult<Self, ValidationError>
where
Self: Sized;
async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError>;
}
#[async_trait::async_trait]
pub trait ReverseConversion<SrcType: Conversion> {
async fn convert(self, key: &Secret<Vec<u8>>) -> CustomResult<SrcType, ValidationError>;
}
#[async_trait::async_trait]
impl<T: Send, U: Conversion<DstType = T>> ReverseConversion<U> for T {
async fn convert(self, key: &Secret<Vec<u8>>) -> CustomResult<U, ValidationError> {
U::convert_back(self, key).await
}
}

View File

@ -11,6 +11,10 @@ pub mod router_flow_types;
pub mod router_request_types;
pub mod router_response_types;
pub mod behaviour;
pub mod merchant_key_store;
pub mod type_encryption;
#[cfg(not(feature = "payouts"))]
pub trait PayoutAttemptInterface {}

View File

@ -0,0 +1,57 @@
use common_utils::{
crypto::{Encryptable, GcmAes256},
custom_serde, date_time,
errors::{CustomResult, ValidationError},
};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use time::PrimitiveDateTime;
use crate::type_encryption::TypeEncryption;
#[derive(Clone, Debug, serde::Serialize)]
pub struct MerchantKeyStore {
pub merchant_id: String,
pub key: Encryptable<Secret<Vec<u8>>>,
#[serde(with = "custom_serde::iso8601")]
pub created_at: PrimitiveDateTime,
}
#[async_trait::async_trait]
impl super::behaviour::Conversion for MerchantKeyStore {
type DstType = diesel_models::merchant_key_store::MerchantKeyStore;
type NewDstType = diesel_models::merchant_key_store::MerchantKeyStoreNew;
async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
Ok(diesel_models::merchant_key_store::MerchantKeyStore {
key: self.key.into(),
merchant_id: self.merchant_id,
created_at: self.created_at,
})
}
async fn convert_back(
item: Self::DstType,
key: &Secret<Vec<u8>>,
) -> CustomResult<Self, ValidationError>
where
Self: Sized,
{
Ok(Self {
key: Encryptable::decrypt(item.key, key.peek(), GcmAes256)
.await
.change_context(ValidationError::InvalidValue {
message: "Failed while decrypting customer data".to_string(),
})?,
merchant_id: item.merchant_id,
created_at: item.created_at,
})
}
async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
Ok(diesel_models::merchant_key_store::MerchantKeyStoreNew {
merchant_id: self.merchant_id,
key: self.key.into(),
created_at: date_time::now(),
})
}
}

View File

@ -11,7 +11,6 @@ use crate::RemoteStorageObject;
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub struct PaymentIntent {
pub id: i32,
pub payment_id: String,
pub merchant_id: String,
pub status: storage_enums::IntentStatus,

View File

@ -1,14 +1,17 @@
use api_models::enums::Connector;
use common_enums as storage_enums;
use common_utils::types::MinorUnit;
use common_utils::{
errors::{CustomResult, ValidationError},
types::MinorUnit,
};
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use super::PaymentIntent;
use crate::{
errors,
behaviour, errors,
mandates::{MandateDataType, MandateDetails},
ForeignIDRef,
ForeignIDRef, RemoteStorageObject,
};
#[async_trait::async_trait]
@ -107,7 +110,6 @@ pub trait PaymentAttemptInterface {
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct PaymentAttempt {
pub id: i32,
pub payment_id: String,
pub merchant_id: String,
pub attempt_id: String,
@ -458,3 +460,164 @@ impl ForeignIDRef for PaymentAttempt {
self.attempt_id.clone()
}
}
use diesel_models::{
PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew,
};
#[async_trait::async_trait]
impl behaviour::Conversion for PaymentIntent {
type DstType = DieselPaymentIntent;
type NewDstType = DieselPaymentIntentNew;
async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
Ok(DieselPaymentIntent {
payment_id: self.payment_id,
merchant_id: self.merchant_id,
status: self.status,
amount: self.amount,
currency: self.currency,
amount_captured: self.amount_captured,
customer_id: self.customer_id,
description: self.description,
return_url: self.return_url,
metadata: self.metadata,
connector_id: self.connector_id,
shipping_address_id: self.shipping_address_id,
billing_address_id: self.billing_address_id,
statement_descriptor_name: self.statement_descriptor_name,
statement_descriptor_suffix: self.statement_descriptor_suffix,
created_at: self.created_at,
modified_at: self.modified_at,
last_synced: self.last_synced,
setup_future_usage: self.setup_future_usage,
off_session: self.off_session,
client_secret: self.client_secret,
active_attempt_id: self.active_attempt.get_id(),
business_country: self.business_country,
business_label: self.business_label,
order_details: self.order_details,
allowed_payment_method_types: self.allowed_payment_method_types,
connector_metadata: self.connector_metadata,
feature_metadata: self.feature_metadata,
attempt_count: self.attempt_count,
profile_id: self.profile_id,
merchant_decision: self.merchant_decision,
payment_link_id: self.payment_link_id,
payment_confirm_source: self.payment_confirm_source,
updated_by: self.updated_by,
surcharge_applicable: self.surcharge_applicable,
request_incremental_authorization: self.request_incremental_authorization,
incremental_authorization_allowed: self.incremental_authorization_allowed,
authorization_count: self.authorization_count,
fingerprint_id: self.fingerprint_id,
session_expiry: self.session_expiry,
request_external_three_ds_authentication: self.request_external_three_ds_authentication,
charges: self.charges,
frm_metadata: self.frm_metadata,
})
}
async fn convert_back(
storage_model: Self::DstType,
_key: &masking::Secret<Vec<u8>>,
) -> CustomResult<Self, ValidationError>
where
Self: Sized,
{
Ok(Self {
payment_id: storage_model.payment_id,
merchant_id: storage_model.merchant_id,
status: storage_model.status,
amount: storage_model.amount,
currency: storage_model.currency,
amount_captured: storage_model.amount_captured,
customer_id: storage_model.customer_id,
description: storage_model.description,
return_url: storage_model.return_url,
metadata: storage_model.metadata,
connector_id: storage_model.connector_id,
shipping_address_id: storage_model.shipping_address_id,
billing_address_id: storage_model.billing_address_id,
statement_descriptor_name: storage_model.statement_descriptor_name,
statement_descriptor_suffix: storage_model.statement_descriptor_suffix,
created_at: storage_model.created_at,
modified_at: storage_model.modified_at,
last_synced: storage_model.last_synced,
setup_future_usage: storage_model.setup_future_usage,
off_session: storage_model.off_session,
client_secret: storage_model.client_secret,
active_attempt: RemoteStorageObject::ForeignID(storage_model.active_attempt_id),
business_country: storage_model.business_country,
business_label: storage_model.business_label,
order_details: storage_model.order_details,
allowed_payment_method_types: storage_model.allowed_payment_method_types,
connector_metadata: storage_model.connector_metadata,
feature_metadata: storage_model.feature_metadata,
attempt_count: storage_model.attempt_count,
profile_id: storage_model.profile_id,
merchant_decision: storage_model.merchant_decision,
payment_link_id: storage_model.payment_link_id,
payment_confirm_source: storage_model.payment_confirm_source,
updated_by: storage_model.updated_by,
surcharge_applicable: storage_model.surcharge_applicable,
request_incremental_authorization: storage_model.request_incremental_authorization,
incremental_authorization_allowed: storage_model.incremental_authorization_allowed,
authorization_count: storage_model.authorization_count,
fingerprint_id: storage_model.fingerprint_id,
session_expiry: storage_model.session_expiry,
request_external_three_ds_authentication: storage_model
.request_external_three_ds_authentication,
charges: storage_model.charges,
frm_metadata: storage_model.frm_metadata,
})
}
async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
Ok(DieselPaymentIntentNew {
payment_id: self.payment_id,
merchant_id: self.merchant_id,
status: self.status,
amount: self.amount,
currency: self.currency,
amount_captured: self.amount_captured,
customer_id: self.customer_id,
description: self.description,
return_url: self.return_url,
metadata: self.metadata,
connector_id: self.connector_id,
shipping_address_id: self.shipping_address_id,
billing_address_id: self.billing_address_id,
statement_descriptor_name: self.statement_descriptor_name,
statement_descriptor_suffix: self.statement_descriptor_suffix,
created_at: Some(self.created_at),
modified_at: Some(self.modified_at),
last_synced: self.last_synced,
setup_future_usage: self.setup_future_usage,
off_session: self.off_session,
client_secret: self.client_secret,
active_attempt_id: self.active_attempt.get_id(),
business_country: self.business_country,
business_label: self.business_label,
order_details: self.order_details,
allowed_payment_method_types: self.allowed_payment_method_types,
connector_metadata: self.connector_metadata,
feature_metadata: self.feature_metadata,
attempt_count: self.attempt_count,
profile_id: self.profile_id,
merchant_decision: self.merchant_decision,
payment_link_id: self.payment_link_id,
payment_confirm_source: self.payment_confirm_source,
updated_by: self.updated_by,
surcharge_applicable: self.surcharge_applicable,
request_incremental_authorization: self.request_incremental_authorization,
incremental_authorization_allowed: self.incremental_authorization_allowed,
authorization_count: self.authorization_count,
fingerprint_id: self.fingerprint_id,
session_expiry: self.session_expiry,
request_external_three_ds_authentication: self.request_external_three_ds_authentication,
charges: self.charges,
frm_metadata: self.frm_metadata,
})
}
}

View File

@ -8,19 +8,21 @@ use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use super::{payment_attempt::PaymentAttempt, PaymentIntent};
use crate::{errors, RemoteStorageObject};
use crate::{errors, merchant_key_store::MerchantKeyStore, RemoteStorageObject};
#[async_trait::async_trait]
pub trait PaymentIntentInterface {
async fn update_payment_intent(
&self,
this: PaymentIntent,
payment_intent: PaymentIntentUpdate,
merchant_key_store: &MerchantKeyStore,
storage_scheme: storage_enums::MerchantStorageScheme,
) -> error_stack::Result<PaymentIntent, errors::StorageError>;
async fn insert_payment_intent(
&self,
new: PaymentIntentNew,
new: PaymentIntent,
merchant_key_store: &MerchantKeyStore,
storage_scheme: storage_enums::MerchantStorageScheme,
) -> error_stack::Result<PaymentIntent, errors::StorageError>;
@ -28,6 +30,7 @@ pub trait PaymentIntentInterface {
&self,
payment_id: &str,
merchant_id: &str,
merchant_key_store: &MerchantKeyStore,
storage_scheme: storage_enums::MerchantStorageScheme,
) -> error_stack::Result<PaymentIntent, errors::StorageError>;
@ -42,6 +45,7 @@ pub trait PaymentIntentInterface {
&self,
merchant_id: &str,
filters: &PaymentIntentFetchConstraints,
merchant_key_store: &MerchantKeyStore,
storage_scheme: storage_enums::MerchantStorageScheme,
) -> error_stack::Result<Vec<PaymentIntent>, errors::StorageError>;
@ -50,6 +54,7 @@ pub trait PaymentIntentInterface {
&self,
merchant_id: &str,
time_range: &api_models::payments::TimeRange,
merchant_key_store: &MerchantKeyStore,
storage_scheme: storage_enums::MerchantStorageScheme,
) -> error_stack::Result<Vec<PaymentIntent>, errors::StorageError>;
@ -58,6 +63,7 @@ pub trait PaymentIntentInterface {
&self,
merchant_id: &str,
constraints: &PaymentIntentFetchConstraints,
merchant_key_store: &MerchantKeyStore,
storage_scheme: storage_enums::MerchantStorageScheme,
) -> error_stack::Result<Vec<(PaymentIntent, PaymentAttempt)>, errors::StorageError>;
@ -440,6 +446,247 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
}
}
use diesel_models::PaymentIntentUpdate as DieselPaymentIntentUpdate;
impl From<PaymentIntentUpdate> for DieselPaymentIntentUpdate {
fn from(value: PaymentIntentUpdate) -> Self {
match value {
PaymentIntentUpdate::ResponseUpdate {
status,
amount_captured,
fingerprint_id,
return_url,
updated_by,
incremental_authorization_allowed,
} => Self::ResponseUpdate {
status,
amount_captured,
fingerprint_id,
return_url,
updated_by,
incremental_authorization_allowed,
},
PaymentIntentUpdate::MetadataUpdate {
metadata,
updated_by,
} => Self::MetadataUpdate {
metadata,
updated_by,
},
PaymentIntentUpdate::ReturnUrlUpdate {
return_url,
status,
customer_id,
shipping_address_id,
billing_address_id,
updated_by,
} => Self::ReturnUrlUpdate {
return_url,
status,
customer_id,
shipping_address_id,
billing_address_id,
updated_by,
},
PaymentIntentUpdate::MerchantStatusUpdate {
status,
shipping_address_id,
billing_address_id,
updated_by,
} => Self::MerchantStatusUpdate {
status,
shipping_address_id,
billing_address_id,
updated_by,
},
PaymentIntentUpdate::PGStatusUpdate {
status,
updated_by,
incremental_authorization_allowed,
} => Self::PGStatusUpdate {
status,
updated_by,
incremental_authorization_allowed,
},
PaymentIntentUpdate::Update {
amount,
currency,
setup_future_usage,
status,
customer_id,
shipping_address_id,
billing_address_id,
return_url,
business_country,
business_label,
description,
statement_descriptor_name,
statement_descriptor_suffix,
order_details,
metadata,
payment_confirm_source,
updated_by,
fingerprint_id,
session_expiry,
request_external_three_ds_authentication,
frm_metadata,
} => Self::Update {
amount,
currency,
setup_future_usage,
status,
customer_id,
shipping_address_id,
billing_address_id,
return_url,
business_country,
business_label,
description,
statement_descriptor_name,
statement_descriptor_suffix,
order_details,
metadata,
payment_confirm_source,
updated_by,
fingerprint_id,
session_expiry,
request_external_three_ds_authentication,
frm_metadata,
},
PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate {
active_attempt_id,
attempt_count,
updated_by,
} => Self::PaymentAttemptAndAttemptCountUpdate {
active_attempt_id,
attempt_count,
updated_by,
},
PaymentIntentUpdate::StatusAndAttemptUpdate {
status,
active_attempt_id,
attempt_count,
updated_by,
} => Self::StatusAndAttemptUpdate {
status,
active_attempt_id,
attempt_count,
updated_by,
},
PaymentIntentUpdate::ApproveUpdate {
status,
merchant_decision,
updated_by,
} => Self::ApproveUpdate {
status,
merchant_decision,
updated_by,
},
PaymentIntentUpdate::RejectUpdate {
status,
merchant_decision,
updated_by,
} => Self::RejectUpdate {
status,
merchant_decision,
updated_by,
},
PaymentIntentUpdate::SurchargeApplicableUpdate {
surcharge_applicable,
updated_by,
} => Self::SurchargeApplicableUpdate {
surcharge_applicable: Some(surcharge_applicable),
updated_by,
},
PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => {
Self::IncrementalAuthorizationAmountUpdate { amount }
}
PaymentIntentUpdate::AuthorizationCountUpdate {
authorization_count,
} => Self::AuthorizationCountUpdate {
authorization_count,
},
PaymentIntentUpdate::CompleteAuthorizeUpdate {
shipping_address_id,
} => Self::CompleteAuthorizeUpdate {
shipping_address_id,
},
}
}
}
impl From<PaymentIntentUpdateInternal> for diesel_models::PaymentIntentUpdateInternal {
fn from(value: PaymentIntentUpdateInternal) -> Self {
let modified_at = Some(common_utils::date_time::now());
let PaymentIntentUpdateInternal {
amount,
currency,
status,
amount_captured,
customer_id,
return_url,
setup_future_usage,
off_session,
metadata,
billing_address_id,
shipping_address_id,
modified_at: _,
active_attempt_id,
business_country,
business_label,
description,
statement_descriptor_name,
statement_descriptor_suffix,
order_details,
attempt_count,
merchant_decision,
payment_confirm_source,
updated_by,
surcharge_applicable,
incremental_authorization_allowed,
authorization_count,
session_expiry,
fingerprint_id,
request_external_three_ds_authentication,
frm_metadata,
} = value;
Self {
amount,
currency,
status,
amount_captured,
customer_id,
return_url,
setup_future_usage,
off_session,
metadata,
billing_address_id,
shipping_address_id,
modified_at,
active_attempt_id,
business_country,
business_label,
description,
statement_descriptor_name,
statement_descriptor_suffix,
order_details,
attempt_count,
merchant_decision,
payment_confirm_source,
updated_by,
surcharge_applicable,
incremental_authorization_allowed,
authorization_count,
session_expiry,
fingerprint_id,
request_external_three_ds_authentication,
frm_metadata,
}
}
}
pub enum PaymentIntentFetchConstraints {
Single { payment_intent_id: String },
List(Box<PaymentIntentListParams>),

View File

@ -0,0 +1,237 @@
use async_trait::async_trait;
use common_utils::{
crypto,
errors::{self, CustomResult},
ext_traits::AsyncExt,
metrics::utils::record_operation_time,
};
use diesel_models::encryption::Encryption;
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use router_env::{instrument, tracing};
#[async_trait]
pub trait TypeEncryption<
T,
V: crypto::EncodeMessage + crypto::DecodeMessage,
S: masking::Strategy<T>,
>: Sized
{
async fn encrypt(
masked_data: Secret<T, S>,
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError>;
async fn decrypt(
encrypted_data: Encryption,
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError>;
}
#[async_trait]
impl<
V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static,
S: masking::Strategy<String> + Send,
> TypeEncryption<String, V, S> for crypto::Encryptable<Secret<String, S>>
{
#[instrument(skip_all)]
async fn encrypt(
masked_data: Secret<String, S>,
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError> {
let encrypted_data = crypt_algo.encode_message(key, masked_data.peek().as_bytes())?;
Ok(Self::new(masked_data, encrypted_data.into()))
}
#[instrument(skip_all)]
async fn decrypt(
encrypted_data: Encryption,
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError> {
let encrypted = encrypted_data.into_inner();
let data = crypt_algo.decode_message(key, encrypted.clone())?;
let value: String = std::str::from_utf8(&data)
.change_context(errors::CryptoError::DecodingFailed)?
.to_string();
Ok(Self::new(value.into(), encrypted))
}
}
#[async_trait]
impl<
V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static,
S: masking::Strategy<serde_json::Value> + Send,
> TypeEncryption<serde_json::Value, V, S>
for crypto::Encryptable<Secret<serde_json::Value, S>>
{
#[instrument(skip_all)]
async fn encrypt(
masked_data: Secret<serde_json::Value, S>,
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError> {
let data = serde_json::to_vec(&masked_data.peek())
.change_context(errors::CryptoError::DecodingFailed)?;
let encrypted_data = crypt_algo.encode_message(key, &data)?;
Ok(Self::new(masked_data, encrypted_data.into()))
}
#[instrument(skip_all)]
async fn decrypt(
encrypted_data: Encryption,
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError> {
let encrypted = encrypted_data.into_inner();
let data = crypt_algo.decode_message(key, encrypted.clone())?;
let value: serde_json::Value =
serde_json::from_slice(&data).change_context(errors::CryptoError::DecodingFailed)?;
Ok(Self::new(value.into(), encrypted))
}
}
#[async_trait]
impl<
V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static,
S: masking::Strategy<Vec<u8>> + Send,
> TypeEncryption<Vec<u8>, V, S> for crypto::Encryptable<Secret<Vec<u8>, S>>
{
#[instrument(skip_all)]
async fn encrypt(
masked_data: Secret<Vec<u8>, S>,
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError> {
let encrypted_data = crypt_algo.encode_message(key, masked_data.peek())?;
Ok(Self::new(masked_data, encrypted_data.into()))
}
#[instrument(skip_all)]
async fn decrypt(
encrypted_data: Encryption,
key: &[u8],
crypt_algo: V,
) -> CustomResult<Self, errors::CryptoError> {
let encrypted = encrypted_data.into_inner();
let data = crypt_algo.decode_message(key, encrypted.clone())?;
Ok(Self::new(data.into(), encrypted))
}
}
pub trait Lift<U> {
type SelfWrapper<T>;
type OtherWrapper<T, E>;
fn lift<Func, E, V>(self, func: Func) -> Self::OtherWrapper<V, E>
where
Func: Fn(Self::SelfWrapper<U>) -> Self::OtherWrapper<V, E>;
}
impl<U> Lift<U> for Option<U> {
type SelfWrapper<T> = Option<T>;
type OtherWrapper<T, E> = CustomResult<Option<T>, E>;
fn lift<Func, E, V>(self, func: Func) -> Self::OtherWrapper<V, E>
where
Func: Fn(Self::SelfWrapper<U>) -> Self::OtherWrapper<V, E>,
{
func(self)
}
}
#[async_trait]
pub trait AsyncLift<U> {
type SelfWrapper<T>;
type OtherWrapper<T, E>;
async fn async_lift<Func, F, E, V>(self, func: Func) -> Self::OtherWrapper<V, E>
where
Func: Fn(Self::SelfWrapper<U>) -> F + Send + Sync,
F: futures::Future<Output = Self::OtherWrapper<V, E>> + Send;
}
#[async_trait]
impl<U, V: Lift<U> + Lift<U, SelfWrapper<U> = V> + Send> AsyncLift<U> for V {
type SelfWrapper<T> = <V as Lift<U>>::SelfWrapper<T>;
type OtherWrapper<T, E> = <V as Lift<U>>::OtherWrapper<T, E>;
async fn async_lift<Func, F, E, W>(self, func: Func) -> Self::OtherWrapper<W, E>
where
Func: Fn(Self::SelfWrapper<U>) -> F + Send + Sync,
F: futures::Future<Output = Self::OtherWrapper<W, E>> + Send,
{
func(self).await
}
}
#[inline]
pub async fn encrypt<E: Clone, S>(
inner: Secret<E, S>,
key: &[u8],
) -> CustomResult<crypto::Encryptable<Secret<E, S>>, errors::CryptoError>
where
S: masking::Strategy<E>,
crypto::Encryptable<Secret<E, S>>: TypeEncryption<E, crypto::GcmAes256, S>,
{
record_operation_time(
crypto::Encryptable::encrypt(inner, key, crypto::GcmAes256),
&metrics::ENCRYPTION_TIME,
&metrics::CONTEXT,
&[],
)
.await
}
#[inline]
pub async fn encrypt_optional<E: Clone, S>(
inner: Option<Secret<E, S>>,
key: &[u8],
) -> CustomResult<Option<crypto::Encryptable<Secret<E, S>>>, errors::CryptoError>
where
Secret<E, S>: Send,
S: masking::Strategy<E>,
crypto::Encryptable<Secret<E, S>>: TypeEncryption<E, crypto::GcmAes256, S>,
{
inner.async_map(|f| encrypt(f, key)).await.transpose()
}
#[inline]
pub async fn decrypt<T: Clone, S: masking::Strategy<T>>(
inner: Option<Encryption>,
key: &[u8],
) -> CustomResult<Option<crypto::Encryptable<Secret<T, S>>>, errors::CryptoError>
where
crypto::Encryptable<Secret<T, S>>: TypeEncryption<T, crypto::GcmAes256, S>,
{
record_operation_time(
inner.async_map(|item| crypto::Encryptable::decrypt(item, key, crypto::GcmAes256)),
&metrics::DECRYPTION_TIME,
&metrics::CONTEXT,
&[],
)
.await
.transpose()
}
pub(crate) mod metrics {
use router_env::{global_meter, histogram_metric, metrics_context, once_cell};
metrics_context!(CONTEXT);
global_meter!(GLOBAL_METER, "ROUTER_API");
// Encryption and Decryption metrics
histogram_metric!(ENCRYPTION_TIME, GLOBAL_METER);
histogram_metric!(DECRYPTION_TIME, GLOBAL_METER);
}