mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 12:15:40 +08:00
1607 lines
64 KiB
Rust
1607 lines
64 KiB
Rust
use std::{fmt::Display, str::FromStr};
|
|
|
|
use api_models::{
|
|
analytics::{frm::FrmTransactionType, refunds::RefundType},
|
|
enums::{DisputeStage, DisputeStatus},
|
|
};
|
|
use common_enums::{
|
|
AuthenticationConnectors, AuthenticationStatus, DecoupledAuthenticationType, TransactionStatus,
|
|
};
|
|
use common_utils::{
|
|
errors::{CustomResult, ParsingError},
|
|
DbConnectionParams,
|
|
};
|
|
use diesel_models::enums::{
|
|
AttemptStatus, AuthenticationType, Currency, FraudCheckStatus, IntentStatus, PaymentMethod,
|
|
RefundStatus, RoutingApproach,
|
|
};
|
|
use error_stack::ResultExt;
|
|
use sqlx::{
|
|
postgres::{PgArgumentBuffer, PgPoolOptions, PgRow, PgTypeInfo, PgValueRef},
|
|
Decode, Encode,
|
|
Error::ColumnNotFound,
|
|
FromRow, Pool, Postgres, Row,
|
|
};
|
|
use storage_impl::config::Database;
|
|
use time::PrimitiveDateTime;
|
|
|
|
use super::{
|
|
health_check::HealthCheck,
|
|
query::{Aggregate, ToSql, Window},
|
|
types::{
|
|
AnalyticsCollection, AnalyticsDataSource, DBEnumWrapper, LoadRow, QueryExecutionError,
|
|
TableEngine,
|
|
},
|
|
};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct SqlxClient {
|
|
pool: Pool<Postgres>,
|
|
}
|
|
|
|
impl Default for SqlxClient {
|
|
fn default() -> Self {
|
|
let database_url = format!(
|
|
"postgres://{}:{}@{}:{}/{}",
|
|
"db_user", "db_pass", "localhost", 5432, "hyperswitch_db"
|
|
);
|
|
Self {
|
|
#[allow(clippy::expect_used)]
|
|
pool: PgPoolOptions::new()
|
|
.connect_lazy(&database_url)
|
|
.expect("SQLX Pool Creation failed"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SqlxClient {
|
|
pub async fn from_conf(conf: &Database, schema: &str) -> Self {
|
|
let database_url = conf.get_database_url(schema);
|
|
#[allow(clippy::expect_used)]
|
|
let pool = PgPoolOptions::new()
|
|
.max_connections(conf.pool_size)
|
|
.acquire_timeout(std::time::Duration::from_secs(conf.connection_timeout))
|
|
.connect_lazy(&database_url)
|
|
.expect("SQLX Pool Creation failed");
|
|
Self { pool }
|
|
}
|
|
}
|
|
|
|
pub trait DbType {
|
|
fn name() -> &'static str;
|
|
}
|
|
|
|
macro_rules! db_type {
|
|
($a: ident, $str: tt) => {
|
|
impl DbType for $a {
|
|
fn name() -> &'static str {
|
|
stringify!($str)
|
|
}
|
|
}
|
|
};
|
|
($a:ident) => {
|
|
impl DbType for $a {
|
|
fn name() -> &'static str {
|
|
stringify!($a)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
db_type!(Currency);
|
|
db_type!(AuthenticationType);
|
|
db_type!(AttemptStatus);
|
|
db_type!(IntentStatus);
|
|
db_type!(PaymentMethod, TEXT);
|
|
db_type!(RefundStatus);
|
|
db_type!(RefundType);
|
|
db_type!(FraudCheckStatus);
|
|
db_type!(FrmTransactionType);
|
|
db_type!(DisputeStage);
|
|
db_type!(DisputeStatus);
|
|
db_type!(AuthenticationStatus);
|
|
db_type!(TransactionStatus);
|
|
db_type!(AuthenticationConnectors);
|
|
db_type!(DecoupledAuthenticationType);
|
|
db_type!(RoutingApproach);
|
|
|
|
impl<'q, Type> Encode<'q, Postgres> for DBEnumWrapper<Type>
|
|
where
|
|
Type: DbType + FromStr + Display,
|
|
{
|
|
fn encode_by_ref(
|
|
&self,
|
|
buf: &mut PgArgumentBuffer,
|
|
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
|
<String as Encode<'q, Postgres>>::encode(self.0.to_string(), buf)
|
|
}
|
|
fn size_hint(&self) -> usize {
|
|
<String as Encode<'q, Postgres>>::size_hint(&self.0.to_string())
|
|
}
|
|
}
|
|
|
|
impl<'r, Type> Decode<'r, Postgres> for DBEnumWrapper<Type>
|
|
where
|
|
Type: DbType + FromStr + Display,
|
|
{
|
|
fn decode(
|
|
value: PgValueRef<'r>,
|
|
) -> Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
|
let str_value = <&'r str as Decode<'r, Postgres>>::decode(value)?;
|
|
Type::from_str(str_value).map(DBEnumWrapper).or(Err(format!(
|
|
"invalid value {:?} for enum {}",
|
|
str_value,
|
|
Type::name()
|
|
)
|
|
.into()))
|
|
}
|
|
}
|
|
|
|
impl<Type> sqlx::Type<Postgres> for DBEnumWrapper<Type>
|
|
where
|
|
Type: DbType + FromStr + Display,
|
|
{
|
|
fn type_info() -> PgTypeInfo {
|
|
PgTypeInfo::with_name(Type::name())
|
|
}
|
|
}
|
|
|
|
impl<T> LoadRow<T> for SqlxClient
|
|
where
|
|
for<'a> T: FromRow<'a, PgRow>,
|
|
{
|
|
fn load_row(row: PgRow) -> CustomResult<T, QueryExecutionError> {
|
|
T::from_row(&row).change_context(QueryExecutionError::RowExtractionFailure)
|
|
}
|
|
}
|
|
|
|
impl super::payments::filters::PaymentFilterAnalytics for SqlxClient {}
|
|
impl super::payments::metrics::PaymentMetricAnalytics for SqlxClient {}
|
|
impl super::payments::distribution::PaymentDistributionAnalytics for SqlxClient {}
|
|
impl super::payment_intents::filters::PaymentIntentFilterAnalytics for SqlxClient {}
|
|
impl super::payment_intents::metrics::PaymentIntentMetricAnalytics for SqlxClient {}
|
|
impl super::refunds::metrics::RefundMetricAnalytics for SqlxClient {}
|
|
impl super::refunds::filters::RefundFilterAnalytics for SqlxClient {}
|
|
impl super::refunds::distribution::RefundDistributionAnalytics for SqlxClient {}
|
|
impl super::disputes::filters::DisputeFilterAnalytics for SqlxClient {}
|
|
impl super::disputes::metrics::DisputeMetricAnalytics for SqlxClient {}
|
|
impl super::frm::metrics::FrmMetricAnalytics for SqlxClient {}
|
|
impl super::frm::filters::FrmFilterAnalytics for SqlxClient {}
|
|
impl super::auth_events::metrics::AuthEventMetricAnalytics for SqlxClient {}
|
|
impl super::auth_events::filters::AuthEventFilterAnalytics for SqlxClient {}
|
|
|
|
#[async_trait::async_trait]
|
|
impl AnalyticsDataSource for SqlxClient {
|
|
type Row = PgRow;
|
|
|
|
async fn load_results<T>(&self, query: &str) -> CustomResult<Vec<T>, QueryExecutionError>
|
|
where
|
|
Self: LoadRow<T>,
|
|
{
|
|
sqlx::query(&format!("{query};"))
|
|
.fetch_all(&self.pool)
|
|
.await
|
|
.change_context(QueryExecutionError::DatabaseError)
|
|
.attach_printable_lazy(|| format!("Failed to run query {query}"))?
|
|
.into_iter()
|
|
.map(Self::load_row)
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.change_context(QueryExecutionError::RowExtractionFailure)
|
|
}
|
|
}
|
|
#[async_trait::async_trait]
|
|
impl HealthCheck for SqlxClient {
|
|
async fn deep_health_check(&self) -> CustomResult<(), QueryExecutionError> {
|
|
sqlx::query("SELECT 1")
|
|
.fetch_all(&self.pool)
|
|
.await
|
|
.map(|_| ())
|
|
.change_context(QueryExecutionError::DatabaseError)
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::auth_events::metrics::AuthEventMetricRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let authentication_status: Option<DBEnumWrapper<AuthenticationStatus>> =
|
|
row.try_get("authentication_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let trans_status: Option<DBEnumWrapper<TransactionStatus>> =
|
|
row.try_get("trans_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_type: Option<DBEnumWrapper<DecoupledAuthenticationType>> =
|
|
row.try_get("authentication_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let error_message: Option<String> = row.try_get("error_message").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_connector: Option<DBEnumWrapper<AuthenticationConnectors>> = row
|
|
.try_get("authentication_connector")
|
|
.or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let message_version: Option<String> =
|
|
row.try_get("message_version").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
|
|
let platform: Option<String> = row.try_get("platform").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let acs_reference_number: Option<String> =
|
|
row.try_get("acs_reference_number").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let count: Option<i64> = row.try_get("count").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
// Removing millisecond precision to get accurate diffs against clickhouse
|
|
let start_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("start_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let end_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("end_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let mcc: Option<String> = row.try_get("mcc").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let merchant_country: Option<String> =
|
|
row.try_get("merchant_country").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let billing_country: Option<String> =
|
|
row.try_get("billing_country").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let shipping_country: Option<String> =
|
|
row.try_get("shipping_country").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let issuer_country: Option<String> =
|
|
row.try_get("issuer_country").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let earliest_supported_version: Option<String> = row
|
|
.try_get("earliest_supported_version")
|
|
.or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let latest_supported_version: Option<String> = row
|
|
.try_get("latest_supported_version")
|
|
.or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let whitelist_decision: Option<bool> =
|
|
row.try_get("whitelist_decision").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_manufacturer: Option<String> =
|
|
row.try_get("device_manufacturer").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_type: Option<String> = row.try_get("device_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_brand: Option<String> = row.try_get("device_brand").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_os: Option<String> = row.try_get("device_os").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_display: Option<String> =
|
|
row.try_get("device_display").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let browser_name: Option<String> = row.try_get("browser_name").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let browser_version: Option<String> =
|
|
row.try_get("browser_version").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let issuer_id: Option<String> = row.try_get("issuer_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let scheme_name: Option<String> = row.try_get("scheme_name").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let exemption_requested: Option<bool> =
|
|
row.try_get("exemption_requested").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let exemption_accepted: Option<bool> =
|
|
row.try_get("exemption_accepted").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
|
|
Ok(Self {
|
|
authentication_status,
|
|
trans_status,
|
|
authentication_type,
|
|
error_message,
|
|
authentication_connector,
|
|
message_version,
|
|
acs_reference_number,
|
|
platform,
|
|
count,
|
|
start_bucket,
|
|
end_bucket,
|
|
mcc,
|
|
currency,
|
|
merchant_country,
|
|
billing_country,
|
|
shipping_country,
|
|
issuer_country,
|
|
earliest_supported_version,
|
|
latest_supported_version,
|
|
whitelist_decision,
|
|
device_manufacturer,
|
|
device_type,
|
|
device_brand,
|
|
device_os,
|
|
device_display,
|
|
browser_name,
|
|
browser_version,
|
|
issuer_id,
|
|
scheme_name,
|
|
exemption_requested,
|
|
exemption_accepted,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::auth_events::filters::AuthEventFilterRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let authentication_status: Option<DBEnumWrapper<AuthenticationStatus>> =
|
|
row.try_get("authentication_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let trans_status: Option<DBEnumWrapper<TransactionStatus>> =
|
|
row.try_get("trans_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_type: Option<DBEnumWrapper<DecoupledAuthenticationType>> =
|
|
row.try_get("authentication_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let error_message: Option<String> = row.try_get("error_message").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_connector: Option<DBEnumWrapper<AuthenticationConnectors>> = row
|
|
.try_get("authentication_connector")
|
|
.or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let message_version: Option<String> =
|
|
row.try_get("message_version").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let acs_reference_number: Option<String> =
|
|
row.try_get("acs_reference_number").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let platform: Option<String> = row.try_get("platform").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let mcc: Option<String> = row.try_get("mcc").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let merchant_country: Option<String> =
|
|
row.try_get("merchant_country").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let billing_country: Option<String> =
|
|
row.try_get("billing_country").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let shipping_country: Option<String> =
|
|
row.try_get("shipping_country").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let issuer_country: Option<String> =
|
|
row.try_get("issuer_country").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let earliest_supported_version: Option<String> = row
|
|
.try_get("earliest_supported_version")
|
|
.or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let latest_supported_version: Option<String> = row
|
|
.try_get("latest_supported_version")
|
|
.or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let whitelist_decision: Option<bool> =
|
|
row.try_get("whitelist_decision").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_manufacturer: Option<String> =
|
|
row.try_get("device_manufacturer").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_type: Option<String> = row.try_get("device_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_brand: Option<String> = row.try_get("device_brand").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_os: Option<String> = row.try_get("device_os").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let device_display: Option<String> =
|
|
row.try_get("device_display").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let browser_name: Option<String> = row.try_get("browser_name").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let browser_version: Option<String> =
|
|
row.try_get("browser_version").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let issuer_id: Option<String> = row.try_get("issuer_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let scheme_name: Option<String> = row.try_get("scheme_name").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let exemption_requested: Option<bool> =
|
|
row.try_get("exemption_requested").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let exemption_accepted: Option<bool> =
|
|
row.try_get("exemption_accepted").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
|
|
Ok(Self {
|
|
authentication_status,
|
|
trans_status,
|
|
authentication_type,
|
|
error_message,
|
|
authentication_connector,
|
|
message_version,
|
|
platform,
|
|
acs_reference_number,
|
|
mcc,
|
|
currency,
|
|
merchant_country,
|
|
billing_country,
|
|
shipping_country,
|
|
issuer_country,
|
|
earliest_supported_version,
|
|
latest_supported_version,
|
|
whitelist_decision,
|
|
device_manufacturer,
|
|
device_type,
|
|
device_brand,
|
|
device_os,
|
|
device_display,
|
|
browser_name,
|
|
browser_version,
|
|
issuer_id,
|
|
scheme_name,
|
|
exemption_requested,
|
|
exemption_accepted,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::refunds::metrics::RefundMetricRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_status: Option<DBEnumWrapper<RefundStatus>> =
|
|
row.try_get("refund_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_type: Option<DBEnumWrapper<RefundType>> =
|
|
row.try_get("refund_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let profile_id: Option<String> = row.try_get("profile_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_reason: Option<String> = row.try_get("refund_reason").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_error_message: Option<String> =
|
|
row.try_get("refund_error_message").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let count: Option<i64> = row.try_get("count").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
// Removing millisecond precision to get accurate diffs against clickhouse
|
|
let start_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("start_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let end_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("end_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
Ok(Self {
|
|
currency,
|
|
refund_status,
|
|
connector,
|
|
refund_type,
|
|
profile_id,
|
|
refund_reason,
|
|
refund_error_message,
|
|
total,
|
|
count,
|
|
start_bucket,
|
|
end_bucket,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::frm::metrics::FrmMetricRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let frm_name: Option<String> = row.try_get("frm_name").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let frm_status: Option<DBEnumWrapper<FraudCheckStatus>> =
|
|
row.try_get("frm_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let frm_transaction_type: Option<DBEnumWrapper<FrmTransactionType>> =
|
|
row.try_get("frm_transaction_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let count: Option<i64> = row.try_get("count").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
// Removing millisecond precision to get accurate diffs against clickhouse
|
|
let start_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("start_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let end_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("end_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
Ok(Self {
|
|
frm_name,
|
|
frm_status,
|
|
frm_transaction_type,
|
|
total,
|
|
count,
|
|
start_bucket,
|
|
end_bucket,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::payments::metrics::PaymentMetricRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let status: Option<DBEnumWrapper<AttemptStatus>> =
|
|
row.try_get("status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_type: Option<DBEnumWrapper<AuthenticationType>> =
|
|
row.try_get("authentication_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method: Option<String> =
|
|
row.try_get("payment_method").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method_type: Option<String> =
|
|
row.try_get("payment_method_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let client_source: Option<String> = row.try_get("client_source").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let client_version: Option<String> =
|
|
row.try_get("client_version").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let profile_id: Option<String> = row.try_get("profile_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_network: Option<String> = row.try_get("card_network").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let merchant_id: Option<String> = row.try_get("merchant_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_last_4: Option<String> = row.try_get("card_last_4").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_issuer: Option<String> = row.try_get("card_issuer").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let error_reason: Option<String> = row.try_get("error_reason").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let first_attempt: Option<bool> = row.try_get("first_attempt").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let routing_approach: Option<DBEnumWrapper<RoutingApproach>> =
|
|
row.try_get("routing_approach").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let signature_network: Option<String> =
|
|
row.try_get("signature_network").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let is_issuer_regulated: Option<bool> =
|
|
row.try_get("is_issuer_regulated").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let is_debit_routed: Option<bool> =
|
|
row.try_get("is_debit_routed").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let count: Option<i64> = row.try_get("count").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
// Removing millisecond precision to get accurate diffs against clickhouse
|
|
let start_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("start_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let end_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("end_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
Ok(Self {
|
|
currency,
|
|
status,
|
|
connector,
|
|
authentication_type,
|
|
payment_method,
|
|
payment_method_type,
|
|
client_source,
|
|
client_version,
|
|
profile_id,
|
|
card_network,
|
|
merchant_id,
|
|
card_last_4,
|
|
card_issuer,
|
|
error_reason,
|
|
first_attempt,
|
|
routing_approach,
|
|
signature_network,
|
|
is_issuer_regulated,
|
|
is_debit_routed,
|
|
total,
|
|
count,
|
|
start_bucket,
|
|
end_bucket,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::payments::distribution::PaymentDistributionRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let status: Option<DBEnumWrapper<AttemptStatus>> =
|
|
row.try_get("status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_type: Option<DBEnumWrapper<AuthenticationType>> =
|
|
row.try_get("authentication_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method: Option<String> =
|
|
row.try_get("payment_method").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method_type: Option<String> =
|
|
row.try_get("payment_method_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let client_source: Option<String> = row.try_get("client_source").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let client_version: Option<String> =
|
|
row.try_get("client_version").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let profile_id: Option<String> = row.try_get("profile_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_network: Option<String> = row.try_get("card_network").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let merchant_id: Option<String> = row.try_get("merchant_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_last_4: Option<String> = row.try_get("card_last_4").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_issuer: Option<String> = row.try_get("card_issuer").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let error_reason: Option<String> = row.try_get("error_reason").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let routing_approach: Option<DBEnumWrapper<RoutingApproach>> =
|
|
row.try_get("routing_approach").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
|
|
let signature_network: Option<String> =
|
|
row.try_get("signature_network").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let is_issuer_regulated: Option<bool> =
|
|
row.try_get("is_issuer_regulated").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let is_debit_routed: Option<bool> =
|
|
row.try_get("is_debit_routed").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let count: Option<i64> = row.try_get("count").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let error_message: Option<String> = row.try_get("error_message").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let first_attempt: Option<bool> = row.try_get("first_attempt").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
// Removing millisecond precision to get accurate diffs against clickhouse
|
|
let start_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("start_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let end_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("end_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
Ok(Self {
|
|
currency,
|
|
status,
|
|
connector,
|
|
authentication_type,
|
|
payment_method,
|
|
payment_method_type,
|
|
client_source,
|
|
client_version,
|
|
profile_id,
|
|
card_network,
|
|
merchant_id,
|
|
card_last_4,
|
|
card_issuer,
|
|
error_reason,
|
|
first_attempt,
|
|
total,
|
|
count,
|
|
error_message,
|
|
routing_approach,
|
|
signature_network,
|
|
is_issuer_regulated,
|
|
is_debit_routed,
|
|
start_bucket,
|
|
end_bucket,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::payments::filters::PaymentFilterRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let status: Option<DBEnumWrapper<AttemptStatus>> =
|
|
row.try_get("status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_type: Option<DBEnumWrapper<AuthenticationType>> =
|
|
row.try_get("authentication_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method: Option<String> =
|
|
row.try_get("payment_method").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method_type: Option<String> =
|
|
row.try_get("payment_method_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let client_source: Option<String> = row.try_get("client_source").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let client_version: Option<String> =
|
|
row.try_get("client_version").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let profile_id: Option<String> = row.try_get("profile_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_network: Option<String> = row.try_get("card_network").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let merchant_id: Option<String> = row.try_get("merchant_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_last_4: Option<String> = row.try_get("card_last_4").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_issuer: Option<String> = row.try_get("card_issuer").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let error_reason: Option<String> = row.try_get("error_reason").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let first_attempt: Option<bool> = row.try_get("first_attempt").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let routing_approach: Option<DBEnumWrapper<RoutingApproach>> =
|
|
row.try_get("routing_approach").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let signature_network: Option<String> =
|
|
row.try_get("signature_network").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let is_issuer_regulated: Option<bool> =
|
|
row.try_get("is_issuer_regulated").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let is_debit_routed: Option<bool> =
|
|
row.try_get("is_debit_routed").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
Ok(Self {
|
|
currency,
|
|
status,
|
|
connector,
|
|
authentication_type,
|
|
payment_method,
|
|
payment_method_type,
|
|
client_source,
|
|
client_version,
|
|
profile_id,
|
|
card_network,
|
|
merchant_id,
|
|
card_last_4,
|
|
card_issuer,
|
|
error_reason,
|
|
first_attempt,
|
|
routing_approach,
|
|
signature_network,
|
|
is_issuer_regulated,
|
|
is_debit_routed,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::payment_intents::metrics::PaymentIntentMetricRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let status: Option<DBEnumWrapper<IntentStatus>> =
|
|
row.try_get("status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let profile_id: Option<String> = row.try_get("profile_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_type: Option<DBEnumWrapper<AuthenticationType>> =
|
|
row.try_get("authentication_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method: Option<String> =
|
|
row.try_get("payment_method").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method_type: Option<String> =
|
|
row.try_get("payment_method_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_network: Option<String> = row.try_get("card_network").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let merchant_id: Option<String> = row.try_get("merchant_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_last_4: Option<String> = row.try_get("card_last_4").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_issuer: Option<String> = row.try_get("card_issuer").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let error_reason: Option<String> = row.try_get("error_reason").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let count: Option<i64> = row.try_get("count").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let first_attempt: Option<i64> = row.try_get("first_attempt").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
// Removing millisecond precision to get accurate diffs against clickhouse
|
|
let start_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("start_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let end_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("end_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
Ok(Self {
|
|
status,
|
|
currency,
|
|
profile_id,
|
|
connector,
|
|
authentication_type,
|
|
payment_method,
|
|
payment_method_type,
|
|
card_network,
|
|
merchant_id,
|
|
card_last_4,
|
|
card_issuer,
|
|
error_reason,
|
|
first_attempt,
|
|
total,
|
|
count,
|
|
start_bucket,
|
|
end_bucket,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::payment_intents::filters::PaymentIntentFilterRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let status: Option<DBEnumWrapper<IntentStatus>> =
|
|
row.try_get("status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let profile_id: Option<String> = row.try_get("profile_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let authentication_type: Option<DBEnumWrapper<AuthenticationType>> =
|
|
row.try_get("authentication_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method: Option<String> =
|
|
row.try_get("payment_method").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let payment_method_type: Option<String> =
|
|
row.try_get("payment_method_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_network: Option<String> = row.try_get("card_network").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let merchant_id: Option<String> = row.try_get("merchant_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_last_4: Option<String> = row.try_get("card_last_4").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let card_issuer: Option<String> = row.try_get("card_issuer").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let error_reason: Option<String> = row.try_get("error_reason").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let customer_id: Option<String> = row.try_get("customer_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
Ok(Self {
|
|
status,
|
|
currency,
|
|
profile_id,
|
|
connector,
|
|
authentication_type,
|
|
payment_method,
|
|
payment_method_type,
|
|
card_network,
|
|
merchant_id,
|
|
card_last_4,
|
|
card_issuer,
|
|
error_reason,
|
|
customer_id,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::refunds::filters::RefundFilterRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_status: Option<DBEnumWrapper<RefundStatus>> =
|
|
row.try_get("refund_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_type: Option<DBEnumWrapper<RefundType>> =
|
|
row.try_get("refund_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let profile_id: Option<String> = row.try_get("profile_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_reason: Option<String> = row.try_get("refund_reason").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_error_message: Option<String> =
|
|
row.try_get("refund_error_message").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
Ok(Self {
|
|
currency,
|
|
refund_status,
|
|
connector,
|
|
refund_type,
|
|
profile_id,
|
|
refund_reason,
|
|
refund_error_message,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::refunds::distribution::RefundDistributionRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_status: Option<DBEnumWrapper<RefundStatus>> =
|
|
row.try_get("refund_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_type: Option<DBEnumWrapper<RefundType>> =
|
|
row.try_get("refund_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let profile_id: Option<String> = row.try_get("profile_id").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let count: Option<i64> = row.try_get("count").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_reason: Option<String> = row.try_get("refund_reason").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let refund_error_message: Option<String> =
|
|
row.try_get("refund_error_message").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
// Removing millisecond precision to get accurate diffs against clickhouse
|
|
let start_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("start_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let end_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("end_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
Ok(Self {
|
|
currency,
|
|
refund_status,
|
|
connector,
|
|
refund_type,
|
|
profile_id,
|
|
total,
|
|
count,
|
|
refund_reason,
|
|
refund_error_message,
|
|
start_bucket,
|
|
end_bucket,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::frm::filters::FrmFilterRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let frm_name: Option<String> = row.try_get("frm_name").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let frm_status: Option<DBEnumWrapper<FraudCheckStatus>> =
|
|
row.try_get("frm_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let frm_transaction_type: Option<DBEnumWrapper<FrmTransactionType>> =
|
|
row.try_get("frm_transaction_type").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
Ok(Self {
|
|
frm_name,
|
|
frm_status,
|
|
frm_transaction_type,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a> FromRow<'a, PgRow> for super::disputes::filters::DisputeFilterRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let dispute_stage: Option<String> = row.try_get("dispute_stage").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let dispute_status: Option<String> =
|
|
row.try_get("dispute_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector_status: Option<String> =
|
|
row.try_get("connector_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
Ok(Self {
|
|
dispute_stage,
|
|
dispute_status,
|
|
connector,
|
|
connector_status,
|
|
currency,
|
|
})
|
|
}
|
|
}
|
|
impl<'a> FromRow<'a, PgRow> for super::disputes::metrics::DisputeMetricRow {
|
|
fn from_row(row: &'a PgRow) -> sqlx::Result<Self> {
|
|
let dispute_stage: Option<DBEnumWrapper<DisputeStage>> =
|
|
row.try_get("dispute_stage").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let dispute_status: Option<DBEnumWrapper<DisputeStatus>> =
|
|
row.try_get("dispute_status").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let connector: Option<String> = row.try_get("connector").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let currency: Option<DBEnumWrapper<Currency>> =
|
|
row.try_get("currency").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let total: Option<bigdecimal::BigDecimal> = row.try_get("total").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
let count: Option<i64> = row.try_get("count").or_else(|e| match e {
|
|
ColumnNotFound(_) => Ok(Default::default()),
|
|
e => Err(e),
|
|
})?;
|
|
// Removing millisecond precision to get accurate diffs against clickhouse
|
|
let start_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("start_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
let end_bucket: Option<PrimitiveDateTime> = row
|
|
.try_get::<Option<PrimitiveDateTime>, _>("end_bucket")?
|
|
.and_then(|dt| dt.replace_millisecond(0).ok());
|
|
Ok(Self {
|
|
dispute_stage,
|
|
dispute_status,
|
|
connector,
|
|
currency,
|
|
total,
|
|
count,
|
|
start_bucket,
|
|
end_bucket,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ToSql<SqlxClient> for PrimitiveDateTime {
|
|
fn to_sql(&self, _table_engine: &TableEngine) -> error_stack::Result<String, ParsingError> {
|
|
Ok(self.to_string())
|
|
}
|
|
}
|
|
|
|
impl ToSql<SqlxClient> for AnalyticsCollection {
|
|
fn to_sql(&self, _table_engine: &TableEngine) -> error_stack::Result<String, ParsingError> {
|
|
match self {
|
|
Self::Payment => Ok("payment_attempt".to_string()),
|
|
Self::PaymentSessionized => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("PaymentSessionized table is not implemented for Sqlx"))?,
|
|
Self::Refund => Ok("refund".to_string()),
|
|
Self::RefundSessionized => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("RefundSessionized table is not implemented for Sqlx"))?,
|
|
Self::SdkEvents => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("SdkEventsAudit table is not implemented for Sqlx"))?,
|
|
Self::SdkEventsAnalytics => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("SdkEvents table is not implemented for Sqlx"))?,
|
|
Self::ApiEvents => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("ApiEvents table is not implemented for Sqlx"))?,
|
|
Self::FraudCheck => Ok("fraud_check".to_string()),
|
|
Self::PaymentIntent => Ok("payment_intent".to_string()),
|
|
Self::PaymentIntentSessionized => Err(error_stack::report!(
|
|
ParsingError::UnknownError
|
|
)
|
|
.attach_printable("PaymentIntentSessionized table is not implemented for Sqlx"))?,
|
|
Self::ConnectorEvents => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("ConnectorEvents table is not implemented for Sqlx"))?,
|
|
Self::ApiEventsAnalytics => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("ApiEvents table is not implemented for Sqlx"))?,
|
|
Self::ActivePaymentsAnalytics => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("ActivePaymentsAnalytics table is not implemented for Sqlx"))?,
|
|
Self::OutgoingWebhookEvent => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("OutgoingWebhookEvents table is not implemented for Sqlx"))?,
|
|
Self::Dispute => Ok("dispute".to_string()),
|
|
Self::DisputeSessionized => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("DisputeSessionized table is not implemented for Sqlx"))?,
|
|
Self::Authentications => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("Authentications table is not implemented for Sqlx"))?,
|
|
Self::RoutingEvents => Err(error_stack::report!(ParsingError::UnknownError)
|
|
.attach_printable("RoutingEvents table is not implemented for Sqlx"))?,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> ToSql<SqlxClient> for Aggregate<T>
|
|
where
|
|
T: ToSql<SqlxClient>,
|
|
{
|
|
fn to_sql(&self, table_engine: &TableEngine) -> error_stack::Result<String, ParsingError> {
|
|
Ok(match self {
|
|
Self::Count { field: _, alias } => {
|
|
format!(
|
|
"count(*){}",
|
|
alias.map_or_else(|| "".to_owned(), |alias| format!(" as {alias}"))
|
|
)
|
|
}
|
|
Self::Sum { field, alias } => {
|
|
format!(
|
|
"sum({}){}",
|
|
field
|
|
.to_sql(table_engine)
|
|
.attach_printable("Failed to sum aggregate")?,
|
|
alias.map_or_else(|| "".to_owned(), |alias| format!(" as {alias}"))
|
|
)
|
|
}
|
|
Self::Min { field, alias } => {
|
|
format!(
|
|
"min({}){}",
|
|
field
|
|
.to_sql(table_engine)
|
|
.attach_printable("Failed to min aggregate")?,
|
|
alias.map_or_else(|| "".to_owned(), |alias| format!(" as {alias}"))
|
|
)
|
|
}
|
|
Self::Max { field, alias } => {
|
|
format!(
|
|
"max({}){}",
|
|
field
|
|
.to_sql(table_engine)
|
|
.attach_printable("Failed to max aggregate")?,
|
|
alias.map_or_else(|| "".to_owned(), |alias| format!(" as {alias}"))
|
|
)
|
|
}
|
|
Self::Percentile {
|
|
field,
|
|
alias,
|
|
percentile,
|
|
} => {
|
|
format!(
|
|
"percentile_cont(0.{}) within group (order by {} asc){}",
|
|
percentile.map_or_else(|| "50".to_owned(), |percentile| percentile.to_string()),
|
|
field
|
|
.to_sql(table_engine)
|
|
.attach_printable("Failed to percentile aggregate")?,
|
|
alias.map_or_else(|| "".to_owned(), |alias| format!(" as {alias}"))
|
|
)
|
|
}
|
|
Self::DistinctCount { field, alias } => {
|
|
format!(
|
|
"count(distinct {}){}",
|
|
field
|
|
.to_sql(table_engine)
|
|
.attach_printable("Failed to distinct count aggregate")?,
|
|
alias.map_or_else(|| "".to_owned(), |alias| format!(" as {alias}"))
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<T> ToSql<SqlxClient> for Window<T>
|
|
where
|
|
T: ToSql<SqlxClient>,
|
|
{
|
|
fn to_sql(&self, table_engine: &TableEngine) -> error_stack::Result<String, ParsingError> {
|
|
Ok(match self {
|
|
Self::Sum {
|
|
field,
|
|
partition_by,
|
|
order_by,
|
|
alias,
|
|
} => {
|
|
format!(
|
|
"sum({}) over ({}{}){}",
|
|
field
|
|
.to_sql(table_engine)
|
|
.attach_printable("Failed to sum window")?,
|
|
partition_by.as_ref().map_or_else(
|
|
|| "".to_owned(),
|
|
|partition_by| format!("partition by {}", partition_by.to_owned())
|
|
),
|
|
order_by.as_ref().map_or_else(
|
|
|| "".to_owned(),
|
|
|(order_column, order)| format!(
|
|
" order by {} {}",
|
|
order_column.to_owned(),
|
|
order
|
|
)
|
|
),
|
|
alias.map_or_else(|| "".to_owned(), |alias| format!(" as {alias}"))
|
|
)
|
|
}
|
|
Self::RowNumber {
|
|
field: _,
|
|
partition_by,
|
|
order_by,
|
|
alias,
|
|
} => {
|
|
format!(
|
|
"row_number() over ({}{}){}",
|
|
partition_by.as_ref().map_or_else(
|
|
|| "".to_owned(),
|
|
|partition_by| format!("partition by {}", partition_by.to_owned())
|
|
),
|
|
order_by.as_ref().map_or_else(
|
|
|| "".to_owned(),
|
|
|(order_column, order)| format!(
|
|
" order by {} {}",
|
|
order_column.to_owned(),
|
|
order
|
|
)
|
|
),
|
|
alias.map_or_else(|| "".to_owned(), |alias| format!(" as {alias}"))
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|