mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +08:00
feat(analytics): three_ds and authentication events in sdkevents (#4251)
Co-authored-by: Akash Kamble <127744130+akash-c-k@users.noreply.github.com>
This commit is contained in:
@ -388,6 +388,7 @@ impl_to_sql_for_to_string!(&DisputeDimensions, DisputeDimensions, DisputeStage);
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FilterTypes {
|
pub enum FilterTypes {
|
||||||
Equal,
|
Equal,
|
||||||
|
NotEqual,
|
||||||
EqualBool,
|
EqualBool,
|
||||||
In,
|
In,
|
||||||
Gte,
|
Gte,
|
||||||
@ -402,6 +403,7 @@ pub fn filter_type_to_sql(l: &String, op: &FilterTypes, r: &String) -> String {
|
|||||||
match op {
|
match op {
|
||||||
FilterTypes::EqualBool => format!("{l} = {r}"),
|
FilterTypes::EqualBool => format!("{l} = {r}"),
|
||||||
FilterTypes::Equal => format!("{l} = '{r}'"),
|
FilterTypes::Equal => format!("{l} = '{r}'"),
|
||||||
|
FilterTypes::NotEqual => format!("{l} != '{r}'"),
|
||||||
FilterTypes::In => format!("{l} IN ({r})"),
|
FilterTypes::In => format!("{l} IN ({r})"),
|
||||||
FilterTypes::Gte => format!("{l} >= '{r}'"),
|
FilterTypes::Gte => format!("{l} >= '{r}'"),
|
||||||
FilterTypes::Gt => format!("{l} > {r}"),
|
FilterTypes::Gt => format!("{l} > {r}"),
|
||||||
|
|||||||
@ -6,13 +6,19 @@ use super::metrics::SdkEventMetricRow;
|
|||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct SdkEventMetricsAccumulator {
|
pub struct SdkEventMetricsAccumulator {
|
||||||
pub payment_attempts: CountAccumulator,
|
pub payment_attempts: CountAccumulator,
|
||||||
pub payment_success: CountAccumulator,
|
|
||||||
pub payment_methods_call_count: CountAccumulator,
|
pub payment_methods_call_count: CountAccumulator,
|
||||||
pub average_payment_time: AverageAccumulator,
|
pub average_payment_time: AverageAccumulator,
|
||||||
pub sdk_initiated_count: CountAccumulator,
|
pub sdk_initiated_count: CountAccumulator,
|
||||||
pub sdk_rendered_count: CountAccumulator,
|
pub sdk_rendered_count: CountAccumulator,
|
||||||
pub payment_method_selected_count: CountAccumulator,
|
pub payment_method_selected_count: CountAccumulator,
|
||||||
pub payment_data_filled_count: CountAccumulator,
|
pub payment_data_filled_count: CountAccumulator,
|
||||||
|
pub three_ds_method_invoked_count: CountAccumulator,
|
||||||
|
pub three_ds_method_skipped_count: CountAccumulator,
|
||||||
|
pub three_ds_method_successful_count: CountAccumulator,
|
||||||
|
pub three_ds_method_unsuccessful_count: CountAccumulator,
|
||||||
|
pub authentication_unsuccessful_count: CountAccumulator,
|
||||||
|
pub three_ds_challenge_flow_count: CountAccumulator,
|
||||||
|
pub three_ds_frictionless_flow_count: CountAccumulator,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -86,13 +92,19 @@ impl SdkEventMetricsAccumulator {
|
|||||||
pub fn collect(self) -> SdkEventMetricsBucketValue {
|
pub fn collect(self) -> SdkEventMetricsBucketValue {
|
||||||
SdkEventMetricsBucketValue {
|
SdkEventMetricsBucketValue {
|
||||||
payment_attempts: self.payment_attempts.collect(),
|
payment_attempts: self.payment_attempts.collect(),
|
||||||
payment_success_count: self.payment_success.collect(),
|
|
||||||
payment_methods_call_count: self.payment_methods_call_count.collect(),
|
payment_methods_call_count: self.payment_methods_call_count.collect(),
|
||||||
average_payment_time: self.average_payment_time.collect(),
|
average_payment_time: self.average_payment_time.collect(),
|
||||||
sdk_initiated_count: self.sdk_initiated_count.collect(),
|
sdk_initiated_count: self.sdk_initiated_count.collect(),
|
||||||
sdk_rendered_count: self.sdk_rendered_count.collect(),
|
sdk_rendered_count: self.sdk_rendered_count.collect(),
|
||||||
payment_method_selected_count: self.payment_method_selected_count.collect(),
|
payment_method_selected_count: self.payment_method_selected_count.collect(),
|
||||||
payment_data_filled_count: self.payment_data_filled_count.collect(),
|
payment_data_filled_count: self.payment_data_filled_count.collect(),
|
||||||
|
three_ds_method_invoked_count: self.three_ds_method_invoked_count.collect(),
|
||||||
|
three_ds_method_skipped_count: self.three_ds_method_skipped_count.collect(),
|
||||||
|
three_ds_method_successful_count: self.three_ds_method_successful_count.collect(),
|
||||||
|
three_ds_method_unsuccessful_count: self.three_ds_method_unsuccessful_count.collect(),
|
||||||
|
authentication_unsuccessful_count: self.authentication_unsuccessful_count.collect(),
|
||||||
|
three_ds_challenge_flow_count: self.three_ds_challenge_flow_count.collect(),
|
||||||
|
three_ds_frictionless_flow_count: self.three_ds_frictionless_flow_count.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,9 +88,6 @@ pub async fn get_metrics(
|
|||||||
SdkEventMetrics::PaymentAttempts => {
|
SdkEventMetrics::PaymentAttempts => {
|
||||||
metrics_builder.payment_attempts.add_metrics_bucket(&value)
|
metrics_builder.payment_attempts.add_metrics_bucket(&value)
|
||||||
}
|
}
|
||||||
SdkEventMetrics::PaymentSuccessCount => {
|
|
||||||
metrics_builder.payment_success.add_metrics_bucket(&value)
|
|
||||||
}
|
|
||||||
SdkEventMetrics::PaymentMethodsCallCount => metrics_builder
|
SdkEventMetrics::PaymentMethodsCallCount => metrics_builder
|
||||||
.payment_methods_call_count
|
.payment_methods_call_count
|
||||||
.add_metrics_bucket(&value),
|
.add_metrics_bucket(&value),
|
||||||
@ -109,6 +106,27 @@ pub async fn get_metrics(
|
|||||||
SdkEventMetrics::AveragePaymentTime => metrics_builder
|
SdkEventMetrics::AveragePaymentTime => metrics_builder
|
||||||
.average_payment_time
|
.average_payment_time
|
||||||
.add_metrics_bucket(&value),
|
.add_metrics_bucket(&value),
|
||||||
|
SdkEventMetrics::ThreeDsMethodInvokedCount => metrics_builder
|
||||||
|
.three_ds_method_invoked_count
|
||||||
|
.add_metrics_bucket(&value),
|
||||||
|
SdkEventMetrics::ThreeDsMethodSkippedCount => metrics_builder
|
||||||
|
.three_ds_method_skipped_count
|
||||||
|
.add_metrics_bucket(&value),
|
||||||
|
SdkEventMetrics::ThreeDsMethodSuccessfulCount => metrics_builder
|
||||||
|
.three_ds_method_successful_count
|
||||||
|
.add_metrics_bucket(&value),
|
||||||
|
SdkEventMetrics::ThreeDsMethodUnsuccessfulCount => metrics_builder
|
||||||
|
.three_ds_method_unsuccessful_count
|
||||||
|
.add_metrics_bucket(&value),
|
||||||
|
SdkEventMetrics::AuthenticationUnsuccessfulCount => metrics_builder
|
||||||
|
.authentication_unsuccessful_count
|
||||||
|
.add_metrics_bucket(&value),
|
||||||
|
SdkEventMetrics::ThreeDsChallengeFlowCount => metrics_builder
|
||||||
|
.three_ds_challenge_flow_count
|
||||||
|
.add_metrics_bucket(&value),
|
||||||
|
SdkEventMetrics::ThreeDsFrictionlessFlowCount => metrics_builder
|
||||||
|
.three_ds_frictionless_flow_count
|
||||||
|
.add_metrics_bucket(&value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,23 +11,35 @@ use crate::{
|
|||||||
types::{AnalyticsCollection, AnalyticsDataSource, LoadRow, MetricsResult},
|
types::{AnalyticsCollection, AnalyticsDataSource, LoadRow, MetricsResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod authentication_unsuccessful_count;
|
||||||
mod average_payment_time;
|
mod average_payment_time;
|
||||||
mod payment_attempts;
|
mod payment_attempts;
|
||||||
mod payment_data_filled_count;
|
mod payment_data_filled_count;
|
||||||
mod payment_method_selected_count;
|
mod payment_method_selected_count;
|
||||||
mod payment_methods_call_count;
|
mod payment_methods_call_count;
|
||||||
mod payment_success_count;
|
|
||||||
mod sdk_initiated_count;
|
mod sdk_initiated_count;
|
||||||
mod sdk_rendered_count;
|
mod sdk_rendered_count;
|
||||||
|
mod three_ds_challenge_flow_count;
|
||||||
|
mod three_ds_frictionless_flow_count;
|
||||||
|
mod three_ds_method_invoked_count;
|
||||||
|
mod three_ds_method_skipped_count;
|
||||||
|
mod three_ds_method_successful_count;
|
||||||
|
mod three_ds_method_unsuccessful_count;
|
||||||
|
|
||||||
|
use authentication_unsuccessful_count::AuthenticationUnsuccessfulCount;
|
||||||
use average_payment_time::AveragePaymentTime;
|
use average_payment_time::AveragePaymentTime;
|
||||||
use payment_attempts::PaymentAttempts;
|
use payment_attempts::PaymentAttempts;
|
||||||
use payment_data_filled_count::PaymentDataFilledCount;
|
use payment_data_filled_count::PaymentDataFilledCount;
|
||||||
use payment_method_selected_count::PaymentMethodSelectedCount;
|
use payment_method_selected_count::PaymentMethodSelectedCount;
|
||||||
use payment_methods_call_count::PaymentMethodsCallCount;
|
use payment_methods_call_count::PaymentMethodsCallCount;
|
||||||
use payment_success_count::PaymentSuccessCount;
|
|
||||||
use sdk_initiated_count::SdkInitiatedCount;
|
use sdk_initiated_count::SdkInitiatedCount;
|
||||||
use sdk_rendered_count::SdkRenderedCount;
|
use sdk_rendered_count::SdkRenderedCount;
|
||||||
|
use three_ds_challenge_flow_count::ThreeDsChallengeFlowCount;
|
||||||
|
use three_ds_frictionless_flow_count::ThreeDsFrictionlessFlowCount;
|
||||||
|
use three_ds_method_invoked_count::ThreeDsMethodInvokedCount;
|
||||||
|
use three_ds_method_skipped_count::ThreeDsMethodSkippedCount;
|
||||||
|
use three_ds_method_successful_count::ThreeDsMethodSuccessfulCount;
|
||||||
|
use three_ds_method_unsuccessful_count::ThreeDsMethodUnsuccessfulCount;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, serde::Deserialize)]
|
#[derive(Debug, PartialEq, Eq, serde::Deserialize)]
|
||||||
pub struct SdkEventMetricRow {
|
pub struct SdkEventMetricRow {
|
||||||
@ -92,18 +104,6 @@ where
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Self::PaymentSuccessCount => {
|
|
||||||
PaymentSuccessCount
|
|
||||||
.load_metrics(
|
|
||||||
dimensions,
|
|
||||||
publishable_key,
|
|
||||||
filters,
|
|
||||||
granularity,
|
|
||||||
time_range,
|
|
||||||
pool,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
Self::PaymentMethodsCallCount => {
|
Self::PaymentMethodsCallCount => {
|
||||||
PaymentMethodsCallCount
|
PaymentMethodsCallCount
|
||||||
.load_metrics(
|
.load_metrics(
|
||||||
@ -176,6 +176,90 @@ where
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
Self::ThreeDsMethodSkippedCount => {
|
||||||
|
ThreeDsMethodSkippedCount
|
||||||
|
.load_metrics(
|
||||||
|
dimensions,
|
||||||
|
publishable_key,
|
||||||
|
filters,
|
||||||
|
granularity,
|
||||||
|
time_range,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Self::ThreeDsMethodInvokedCount => {
|
||||||
|
ThreeDsMethodInvokedCount
|
||||||
|
.load_metrics(
|
||||||
|
dimensions,
|
||||||
|
publishable_key,
|
||||||
|
filters,
|
||||||
|
granularity,
|
||||||
|
time_range,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Self::ThreeDsMethodSuccessfulCount => {
|
||||||
|
ThreeDsMethodSuccessfulCount
|
||||||
|
.load_metrics(
|
||||||
|
dimensions,
|
||||||
|
publishable_key,
|
||||||
|
filters,
|
||||||
|
granularity,
|
||||||
|
time_range,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Self::ThreeDsMethodUnsuccessfulCount => {
|
||||||
|
ThreeDsMethodUnsuccessfulCount
|
||||||
|
.load_metrics(
|
||||||
|
dimensions,
|
||||||
|
publishable_key,
|
||||||
|
filters,
|
||||||
|
granularity,
|
||||||
|
time_range,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Self::AuthenticationUnsuccessfulCount => {
|
||||||
|
AuthenticationUnsuccessfulCount
|
||||||
|
.load_metrics(
|
||||||
|
dimensions,
|
||||||
|
publishable_key,
|
||||||
|
filters,
|
||||||
|
granularity,
|
||||||
|
time_range,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Self::ThreeDsChallengeFlowCount => {
|
||||||
|
ThreeDsChallengeFlowCount
|
||||||
|
.load_metrics(
|
||||||
|
dimensions,
|
||||||
|
publishable_key,
|
||||||
|
filters,
|
||||||
|
granularity,
|
||||||
|
time_range,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Self::ThreeDsFrictionlessFlowCount => {
|
||||||
|
ThreeDsFrictionlessFlowCount
|
||||||
|
.load_metrics(
|
||||||
|
dimensions,
|
||||||
|
publishable_key,
|
||||||
|
filters,
|
||||||
|
granularity,
|
||||||
|
time_range,
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,122 @@
|
|||||||
|
use api_models::analytics::{
|
||||||
|
sdk_events::{
|
||||||
|
SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
|
||||||
|
},
|
||||||
|
Granularity, TimeRange,
|
||||||
|
};
|
||||||
|
use common_utils::errors::ReportSwitchExt;
|
||||||
|
use error_stack::ResultExt;
|
||||||
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
|
use super::SdkEventMetricRow;
|
||||||
|
use crate::{
|
||||||
|
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||||
|
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) struct AuthenticationUnsuccessfulCount;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<T> super::SdkEventMetric<T> for AuthenticationUnsuccessfulCount
|
||||||
|
where
|
||||||
|
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
||||||
|
PrimitiveDateTime: ToSql<T>,
|
||||||
|
AnalyticsCollection: ToSql<T>,
|
||||||
|
Granularity: GroupByClause<T>,
|
||||||
|
Aggregate<&'static str>: ToSql<T>,
|
||||||
|
Window<&'static str>: ToSql<T>,
|
||||||
|
{
|
||||||
|
async fn load_metrics(
|
||||||
|
&self,
|
||||||
|
dimensions: &[SdkEventDimensions],
|
||||||
|
publishable_key: &str,
|
||||||
|
filters: &SdkEventFilters,
|
||||||
|
granularity: &Option<Granularity>,
|
||||||
|
time_range: &TimeRange,
|
||||||
|
pool: &T,
|
||||||
|
) -> MetricsResult<Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
|
||||||
|
let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::SdkEvents);
|
||||||
|
let dimensions = dimensions.to_vec();
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder.add_select_column(dim).switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_select_column(Aggregate::Count {
|
||||||
|
field: None,
|
||||||
|
alias: Some("count"),
|
||||||
|
})
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
if let Some(granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_granularity_in_mins(granularity)
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.set_filter_clause(&mut query_builder).switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("merchant_id", publishable_key)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("event_name", SdkEventNames::AuthenticationCall)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("log_type", "ERROR")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("category", "USER_EVENT")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
time_range
|
||||||
|
.set_filter_clause(&mut query_builder)
|
||||||
|
.attach_printable("Error filtering time range")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause(dim)
|
||||||
|
.attach_printable("Error grouping by dimensions")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause("time_bucket")
|
||||||
|
.attach_printable("Error adding granularity")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.execute_query::<SdkEventMetricRow, _>(pool)
|
||||||
|
.await
|
||||||
|
.change_context(MetricsError::QueryBuildingError)?
|
||||||
|
.change_context(MetricsError::QueryExecutionFailure)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| {
|
||||||
|
Ok((
|
||||||
|
SdkEventMetricsBucketIdentifier::new(
|
||||||
|
i.payment_method.clone(),
|
||||||
|
i.platform.clone(),
|
||||||
|
i.browser_name.clone(),
|
||||||
|
i.source.clone(),
|
||||||
|
i.component.clone(),
|
||||||
|
i.payment_experience.clone(),
|
||||||
|
i.time_bucket.clone(),
|
||||||
|
),
|
||||||
|
i,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<error_stack::Result<
|
||||||
|
Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
|
||||||
|
crate::query::PostProcessingError,
|
||||||
|
>>()
|
||||||
|
.change_context(MetricsError::PostProcessingFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -67,7 +67,7 @@ where
|
|||||||
.switch()?;
|
.switch()?;
|
||||||
|
|
||||||
query_builder
|
query_builder
|
||||||
.add_filter_clause("event_name", SdkEventNames::StripeElementsCalled)
|
.add_filter_clause("event_name", SdkEventNames::OrcaElementsCalled)
|
||||||
.switch()?;
|
.switch()?;
|
||||||
|
|
||||||
time_range
|
time_range
|
||||||
|
|||||||
@ -0,0 +1,124 @@
|
|||||||
|
use api_models::analytics::{
|
||||||
|
sdk_events::{
|
||||||
|
SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
|
||||||
|
},
|
||||||
|
Granularity, TimeRange,
|
||||||
|
};
|
||||||
|
use common_utils::errors::ReportSwitchExt;
|
||||||
|
use error_stack::ResultExt;
|
||||||
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
|
use super::SdkEventMetricRow;
|
||||||
|
use crate::{
|
||||||
|
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||||
|
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) struct ThreeDsChallengeFlowCount;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<T> super::SdkEventMetric<T> for ThreeDsChallengeFlowCount
|
||||||
|
where
|
||||||
|
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
||||||
|
PrimitiveDateTime: ToSql<T>,
|
||||||
|
AnalyticsCollection: ToSql<T>,
|
||||||
|
Granularity: GroupByClause<T>,
|
||||||
|
Aggregate<&'static str>: ToSql<T>,
|
||||||
|
Window<&'static str>: ToSql<T>,
|
||||||
|
{
|
||||||
|
async fn load_metrics(
|
||||||
|
&self,
|
||||||
|
dimensions: &[SdkEventDimensions],
|
||||||
|
publishable_key: &str,
|
||||||
|
filters: &SdkEventFilters,
|
||||||
|
granularity: &Option<Granularity>,
|
||||||
|
time_range: &TimeRange,
|
||||||
|
pool: &T,
|
||||||
|
) -> MetricsResult<Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
|
||||||
|
let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::SdkEvents);
|
||||||
|
let dimensions = dimensions.to_vec();
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder.add_select_column(dim).switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_select_column(Aggregate::Count {
|
||||||
|
field: None,
|
||||||
|
alias: Some("count"),
|
||||||
|
})
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
if let Some(granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_granularity_in_mins(granularity)
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.set_filter_clause(&mut query_builder).switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("merchant_id", publishable_key)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("event_name", SdkEventNames::DisplayThreeDsSdk)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("log_type", "INFO")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("category", "USER_EVENT")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder.add_filter_clause("value", "C").switch()?;
|
||||||
|
|
||||||
|
time_range
|
||||||
|
.set_filter_clause(&mut query_builder)
|
||||||
|
.attach_printable("Error filtering time range")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause(dim)
|
||||||
|
.attach_printable("Error grouping by dimensions")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause("time_bucket")
|
||||||
|
.attach_printable("Error adding granularity")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.execute_query::<SdkEventMetricRow, _>(pool)
|
||||||
|
.await
|
||||||
|
.change_context(MetricsError::QueryBuildingError)?
|
||||||
|
.change_context(MetricsError::QueryExecutionFailure)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| {
|
||||||
|
Ok((
|
||||||
|
SdkEventMetricsBucketIdentifier::new(
|
||||||
|
i.payment_method.clone(),
|
||||||
|
i.platform.clone(),
|
||||||
|
i.browser_name.clone(),
|
||||||
|
i.source.clone(),
|
||||||
|
i.component.clone(),
|
||||||
|
i.payment_experience.clone(),
|
||||||
|
i.time_bucket.clone(),
|
||||||
|
),
|
||||||
|
i,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<error_stack::Result<
|
||||||
|
Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
|
||||||
|
crate::query::PostProcessingError,
|
||||||
|
>>()
|
||||||
|
.change_context(MetricsError::PostProcessingFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
use api_models::analytics::{
|
||||||
|
sdk_events::{
|
||||||
|
SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
|
||||||
|
},
|
||||||
|
Granularity, TimeRange,
|
||||||
|
};
|
||||||
|
use common_utils::errors::ReportSwitchExt;
|
||||||
|
use error_stack::ResultExt;
|
||||||
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
|
use super::SdkEventMetricRow;
|
||||||
|
use crate::{
|
||||||
|
query::{Aggregate, FilterTypes, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||||
|
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) struct ThreeDsFrictionlessFlowCount;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<T> super::SdkEventMetric<T> for ThreeDsFrictionlessFlowCount
|
||||||
|
where
|
||||||
|
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
||||||
|
PrimitiveDateTime: ToSql<T>,
|
||||||
|
AnalyticsCollection: ToSql<T>,
|
||||||
|
Granularity: GroupByClause<T>,
|
||||||
|
Aggregate<&'static str>: ToSql<T>,
|
||||||
|
Window<&'static str>: ToSql<T>,
|
||||||
|
{
|
||||||
|
async fn load_metrics(
|
||||||
|
&self,
|
||||||
|
dimensions: &[SdkEventDimensions],
|
||||||
|
publishable_key: &str,
|
||||||
|
filters: &SdkEventFilters,
|
||||||
|
granularity: &Option<Granularity>,
|
||||||
|
time_range: &TimeRange,
|
||||||
|
pool: &T,
|
||||||
|
) -> MetricsResult<Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
|
||||||
|
let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::SdkEvents);
|
||||||
|
let dimensions = dimensions.to_vec();
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder.add_select_column(dim).switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_select_column(Aggregate::Count {
|
||||||
|
field: None,
|
||||||
|
alias: Some("count"),
|
||||||
|
})
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
if let Some(granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_granularity_in_mins(granularity)
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.set_filter_clause(&mut query_builder).switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("merchant_id", publishable_key)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("event_name", SdkEventNames::DisplayThreeDsSdk)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("log_type", "INFO")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("category", "USER_EVENT")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_custom_filter_clause("value", "C", FilterTypes::NotEqual)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
time_range
|
||||||
|
.set_filter_clause(&mut query_builder)
|
||||||
|
.attach_printable("Error filtering time range")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause(dim)
|
||||||
|
.attach_printable("Error grouping by dimensions")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause("time_bucket")
|
||||||
|
.attach_printable("Error adding granularity")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.execute_query::<SdkEventMetricRow, _>(pool)
|
||||||
|
.await
|
||||||
|
.change_context(MetricsError::QueryBuildingError)?
|
||||||
|
.change_context(MetricsError::QueryExecutionFailure)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| {
|
||||||
|
Ok((
|
||||||
|
SdkEventMetricsBucketIdentifier::new(
|
||||||
|
i.payment_method.clone(),
|
||||||
|
i.platform.clone(),
|
||||||
|
i.browser_name.clone(),
|
||||||
|
i.source.clone(),
|
||||||
|
i.component.clone(),
|
||||||
|
i.payment_experience.clone(),
|
||||||
|
i.time_bucket.clone(),
|
||||||
|
),
|
||||||
|
i,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<error_stack::Result<
|
||||||
|
Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
|
||||||
|
crate::query::PostProcessingError,
|
||||||
|
>>()
|
||||||
|
.change_context(MetricsError::PostProcessingFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
use api_models::analytics::{
|
||||||
|
sdk_events::{
|
||||||
|
SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
|
||||||
|
},
|
||||||
|
Granularity, TimeRange,
|
||||||
|
};
|
||||||
|
use common_utils::errors::ReportSwitchExt;
|
||||||
|
use error_stack::ResultExt;
|
||||||
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
|
use super::SdkEventMetricRow;
|
||||||
|
use crate::{
|
||||||
|
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||||
|
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) struct ThreeDsMethodInvokedCount;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<T> super::SdkEventMetric<T> for ThreeDsMethodInvokedCount
|
||||||
|
where
|
||||||
|
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
||||||
|
PrimitiveDateTime: ToSql<T>,
|
||||||
|
AnalyticsCollection: ToSql<T>,
|
||||||
|
Granularity: GroupByClause<T>,
|
||||||
|
Aggregate<&'static str>: ToSql<T>,
|
||||||
|
Window<&'static str>: ToSql<T>,
|
||||||
|
{
|
||||||
|
async fn load_metrics(
|
||||||
|
&self,
|
||||||
|
dimensions: &[SdkEventDimensions],
|
||||||
|
publishable_key: &str,
|
||||||
|
filters: &SdkEventFilters,
|
||||||
|
granularity: &Option<Granularity>,
|
||||||
|
time_range: &TimeRange,
|
||||||
|
pool: &T,
|
||||||
|
) -> MetricsResult<Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
|
||||||
|
let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::SdkEvents);
|
||||||
|
let dimensions = dimensions.to_vec();
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder.add_select_column(dim).switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_select_column(Aggregate::Count {
|
||||||
|
field: None,
|
||||||
|
alias: Some("count"),
|
||||||
|
})
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
if let Some(granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_granularity_in_mins(granularity)
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.set_filter_clause(&mut query_builder).switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("merchant_id", publishable_key)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("event_name", SdkEventNames::ThreeDsMethod)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("log_type", "INFO")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("category", "USER_EVENT")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder.add_filter_clause("value", "Y").switch()?;
|
||||||
|
|
||||||
|
time_range
|
||||||
|
.set_filter_clause(&mut query_builder)
|
||||||
|
.attach_printable("Error filtering time range")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause(dim)
|
||||||
|
.attach_printable("Error grouping by dimensions")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause("time_bucket")
|
||||||
|
.attach_printable("Error adding granularity")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.execute_query::<SdkEventMetricRow, _>(pool)
|
||||||
|
.await
|
||||||
|
.change_context(MetricsError::QueryBuildingError)?
|
||||||
|
.change_context(MetricsError::QueryExecutionFailure)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| {
|
||||||
|
Ok((
|
||||||
|
SdkEventMetricsBucketIdentifier::new(
|
||||||
|
i.payment_method.clone(),
|
||||||
|
i.platform.clone(),
|
||||||
|
i.browser_name.clone(),
|
||||||
|
i.source.clone(),
|
||||||
|
i.component.clone(),
|
||||||
|
i.payment_experience.clone(),
|
||||||
|
i.time_bucket.clone(),
|
||||||
|
),
|
||||||
|
i,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<error_stack::Result<
|
||||||
|
Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
|
||||||
|
crate::query::PostProcessingError,
|
||||||
|
>>()
|
||||||
|
.change_context(MetricsError::PostProcessingFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
use api_models::analytics::{
|
||||||
|
sdk_events::{
|
||||||
|
SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
|
||||||
|
},
|
||||||
|
Granularity, TimeRange,
|
||||||
|
};
|
||||||
|
use common_utils::errors::ReportSwitchExt;
|
||||||
|
use error_stack::ResultExt;
|
||||||
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
|
use super::SdkEventMetricRow;
|
||||||
|
use crate::{
|
||||||
|
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||||
|
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) struct ThreeDsMethodSkippedCount;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<T> super::SdkEventMetric<T> for ThreeDsMethodSkippedCount
|
||||||
|
where
|
||||||
|
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
||||||
|
PrimitiveDateTime: ToSql<T>,
|
||||||
|
AnalyticsCollection: ToSql<T>,
|
||||||
|
Granularity: GroupByClause<T>,
|
||||||
|
Aggregate<&'static str>: ToSql<T>,
|
||||||
|
Window<&'static str>: ToSql<T>,
|
||||||
|
{
|
||||||
|
async fn load_metrics(
|
||||||
|
&self,
|
||||||
|
dimensions: &[SdkEventDimensions],
|
||||||
|
publishable_key: &str,
|
||||||
|
filters: &SdkEventFilters,
|
||||||
|
granularity: &Option<Granularity>,
|
||||||
|
time_range: &TimeRange,
|
||||||
|
pool: &T,
|
||||||
|
) -> MetricsResult<Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
|
||||||
|
let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::SdkEvents);
|
||||||
|
let dimensions = dimensions.to_vec();
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder.add_select_column(dim).switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_select_column(Aggregate::Count {
|
||||||
|
field: None,
|
||||||
|
alias: Some("count"),
|
||||||
|
})
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
if let Some(granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_granularity_in_mins(granularity)
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.set_filter_clause(&mut query_builder).switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("merchant_id", publishable_key)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("event_name", SdkEventNames::ThreeDsMethod)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("log_type", "INFO")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("category", "USER_EVENT")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder.add_filter_clause("value", "N").switch()?;
|
||||||
|
|
||||||
|
time_range
|
||||||
|
.set_filter_clause(&mut query_builder)
|
||||||
|
.attach_printable("Error filtering time range")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause(dim)
|
||||||
|
.attach_printable("Error grouping by dimensions")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause("time_bucket")
|
||||||
|
.attach_printable("Error adding granularity")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.execute_query::<SdkEventMetricRow, _>(pool)
|
||||||
|
.await
|
||||||
|
.change_context(MetricsError::QueryBuildingError)?
|
||||||
|
.change_context(MetricsError::QueryExecutionFailure)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| {
|
||||||
|
Ok((
|
||||||
|
SdkEventMetricsBucketIdentifier::new(
|
||||||
|
i.payment_method.clone(),
|
||||||
|
i.platform.clone(),
|
||||||
|
i.browser_name.clone(),
|
||||||
|
i.source.clone(),
|
||||||
|
i.component.clone(),
|
||||||
|
i.payment_experience.clone(),
|
||||||
|
i.time_bucket.clone(),
|
||||||
|
),
|
||||||
|
i,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<error_stack::Result<
|
||||||
|
Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
|
||||||
|
crate::query::PostProcessingError,
|
||||||
|
>>()
|
||||||
|
.change_context(MetricsError::PostProcessingFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,10 +15,10 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(super) struct PaymentSuccessCount;
|
pub(super) struct ThreeDsMethodSuccessfulCount;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<T> super::SdkEventMetric<T> for PaymentSuccessCount
|
impl<T> super::SdkEventMetric<T> for ThreeDsMethodSuccessfulCount
|
||||||
where
|
where
|
||||||
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
||||||
PrimitiveDateTime: ToSql<T>,
|
PrimitiveDateTime: ToSql<T>,
|
||||||
@ -63,11 +63,15 @@ where
|
|||||||
.switch()?;
|
.switch()?;
|
||||||
|
|
||||||
query_builder
|
query_builder
|
||||||
.add_bool_filter_clause("first_event", 1)
|
.add_filter_clause("event_name", SdkEventNames::ThreeDsMethodResult)
|
||||||
.switch()?;
|
.switch()?;
|
||||||
|
|
||||||
query_builder
|
query_builder
|
||||||
.add_filter_clause("event_name", SdkEventNames::PaymentSuccess)
|
.add_filter_clause("log_type", "INFO")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("category", "USER_EVENT")
|
||||||
.switch()?;
|
.switch()?;
|
||||||
|
|
||||||
time_range
|
time_range
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
use api_models::analytics::{
|
||||||
|
sdk_events::{
|
||||||
|
SdkEventDimensions, SdkEventFilters, SdkEventMetricsBucketIdentifier, SdkEventNames,
|
||||||
|
},
|
||||||
|
Granularity, TimeRange,
|
||||||
|
};
|
||||||
|
use common_utils::errors::ReportSwitchExt;
|
||||||
|
use error_stack::ResultExt;
|
||||||
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
|
use super::SdkEventMetricRow;
|
||||||
|
use crate::{
|
||||||
|
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||||
|
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(super) struct ThreeDsMethodUnsuccessfulCount;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<T> super::SdkEventMetric<T> for ThreeDsMethodUnsuccessfulCount
|
||||||
|
where
|
||||||
|
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
||||||
|
PrimitiveDateTime: ToSql<T>,
|
||||||
|
AnalyticsCollection: ToSql<T>,
|
||||||
|
Granularity: GroupByClause<T>,
|
||||||
|
Aggregate<&'static str>: ToSql<T>,
|
||||||
|
Window<&'static str>: ToSql<T>,
|
||||||
|
{
|
||||||
|
async fn load_metrics(
|
||||||
|
&self,
|
||||||
|
dimensions: &[SdkEventDimensions],
|
||||||
|
publishable_key: &str,
|
||||||
|
filters: &SdkEventFilters,
|
||||||
|
granularity: &Option<Granularity>,
|
||||||
|
time_range: &TimeRange,
|
||||||
|
pool: &T,
|
||||||
|
) -> MetricsResult<Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>> {
|
||||||
|
let mut query_builder: QueryBuilder<T> = QueryBuilder::new(AnalyticsCollection::SdkEvents);
|
||||||
|
let dimensions = dimensions.to_vec();
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder.add_select_column(dim).switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_select_column(Aggregate::Count {
|
||||||
|
field: None,
|
||||||
|
alias: Some("count"),
|
||||||
|
})
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
if let Some(granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_granularity_in_mins(granularity)
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.set_filter_clause(&mut query_builder).switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("merchant_id", publishable_key)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("event_name", SdkEventNames::ThreeDsMethodResult)
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("log_type", "ERROR")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.add_filter_clause("category", "USER_EVENT")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
time_range
|
||||||
|
.set_filter_clause(&mut query_builder)
|
||||||
|
.attach_printable("Error filtering time range")
|
||||||
|
.switch()?;
|
||||||
|
|
||||||
|
for dim in dimensions.iter() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause(dim)
|
||||||
|
.attach_printable("Error grouping by dimensions")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_granularity) = granularity.as_ref() {
|
||||||
|
query_builder
|
||||||
|
.add_group_by_clause("time_bucket")
|
||||||
|
.attach_printable("Error adding granularity")
|
||||||
|
.switch()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder
|
||||||
|
.execute_query::<SdkEventMetricRow, _>(pool)
|
||||||
|
.await
|
||||||
|
.change_context(MetricsError::QueryBuildingError)?
|
||||||
|
.change_context(MetricsError::QueryExecutionFailure)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| {
|
||||||
|
Ok((
|
||||||
|
SdkEventMetricsBucketIdentifier::new(
|
||||||
|
i.payment_method.clone(),
|
||||||
|
i.platform.clone(),
|
||||||
|
i.browser_name.clone(),
|
||||||
|
i.source.clone(),
|
||||||
|
i.component.clone(),
|
||||||
|
i.payment_experience.clone(),
|
||||||
|
i.time_bucket.clone(),
|
||||||
|
),
|
||||||
|
i,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<error_stack::Result<
|
||||||
|
Vec<(SdkEventMetricsBucketIdentifier, SdkEventMetricRow)>,
|
||||||
|
crate::query::PostProcessingError,
|
||||||
|
>>()
|
||||||
|
.change_context(MetricsError::PostProcessingFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -71,8 +71,14 @@ pub enum SdkEventDimensions {
|
|||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum SdkEventMetrics {
|
pub enum SdkEventMetrics {
|
||||||
PaymentAttempts,
|
PaymentAttempts,
|
||||||
PaymentSuccessCount,
|
|
||||||
PaymentMethodsCallCount,
|
PaymentMethodsCallCount,
|
||||||
|
ThreeDsMethodInvokedCount,
|
||||||
|
ThreeDsMethodSkippedCount,
|
||||||
|
ThreeDsMethodSuccessfulCount,
|
||||||
|
ThreeDsMethodUnsuccessfulCount,
|
||||||
|
AuthenticationUnsuccessfulCount,
|
||||||
|
ThreeDsChallengeFlowCount,
|
||||||
|
ThreeDsFrictionlessFlowCount,
|
||||||
SdkRenderedCount,
|
SdkRenderedCount,
|
||||||
SdkInitiatedCount,
|
SdkInitiatedCount,
|
||||||
PaymentMethodSelectedCount,
|
PaymentMethodSelectedCount,
|
||||||
@ -95,12 +101,11 @@ pub enum SdkEventMetrics {
|
|||||||
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
|
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
pub enum SdkEventNames {
|
pub enum SdkEventNames {
|
||||||
StripeElementsCalled,
|
OrcaElementsCalled,
|
||||||
AppRendered,
|
AppRendered,
|
||||||
PaymentMethodChanged,
|
PaymentMethodChanged,
|
||||||
PaymentDataFilled,
|
PaymentDataFilled,
|
||||||
PaymentAttempt,
|
PaymentAttempt,
|
||||||
PaymentSuccess,
|
|
||||||
PaymentMethodsCall,
|
PaymentMethodsCall,
|
||||||
ConfirmCall,
|
ConfirmCall,
|
||||||
SessionsCall,
|
SessionsCall,
|
||||||
@ -108,12 +113,24 @@ pub enum SdkEventNames {
|
|||||||
RedirectingUser,
|
RedirectingUser,
|
||||||
DisplayBankTransferInfoPage,
|
DisplayBankTransferInfoPage,
|
||||||
DisplayQrCodeInfoPage,
|
DisplayQrCodeInfoPage,
|
||||||
|
AuthenticationCall,
|
||||||
|
ThreeDsMethodCall,
|
||||||
|
ThreeDsMethodResult,
|
||||||
|
ThreeDsMethod,
|
||||||
|
LoaderChanged,
|
||||||
|
DisplayThreeDsSdk,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod metric_behaviour {
|
pub mod metric_behaviour {
|
||||||
pub struct PaymentAttempts;
|
pub struct PaymentAttempts;
|
||||||
pub struct PaymentSuccessCount;
|
|
||||||
pub struct PaymentMethodsCallCount;
|
pub struct PaymentMethodsCallCount;
|
||||||
|
pub struct ThreeDsMethodInvokedCount;
|
||||||
|
pub struct ThreeDsMethodSkippedCount;
|
||||||
|
pub struct ThreeDsMethodSuccessfulCount;
|
||||||
|
pub struct ThreeDsMethodUnsuccessfulCount;
|
||||||
|
pub struct AuthenticationUnsuccessfulCount;
|
||||||
|
pub struct ThreeDsChallengeFlowCount;
|
||||||
|
pub struct ThreeDsFrictionlessFlowCount;
|
||||||
pub struct SdkRenderedCount;
|
pub struct SdkRenderedCount;
|
||||||
pub struct SdkInitiatedCount;
|
pub struct SdkInitiatedCount;
|
||||||
pub struct PaymentMethodSelectedCount;
|
pub struct PaymentMethodSelectedCount;
|
||||||
@ -197,13 +214,19 @@ impl PartialEq for SdkEventMetricsBucketIdentifier {
|
|||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
pub struct SdkEventMetricsBucketValue {
|
pub struct SdkEventMetricsBucketValue {
|
||||||
pub payment_attempts: Option<u64>,
|
pub payment_attempts: Option<u64>,
|
||||||
pub payment_success_count: Option<u64>,
|
|
||||||
pub payment_methods_call_count: Option<u64>,
|
pub payment_methods_call_count: Option<u64>,
|
||||||
pub average_payment_time: Option<f64>,
|
pub average_payment_time: Option<f64>,
|
||||||
pub sdk_rendered_count: Option<u64>,
|
pub sdk_rendered_count: Option<u64>,
|
||||||
pub sdk_initiated_count: Option<u64>,
|
pub sdk_initiated_count: Option<u64>,
|
||||||
pub payment_method_selected_count: Option<u64>,
|
pub payment_method_selected_count: Option<u64>,
|
||||||
pub payment_data_filled_count: Option<u64>,
|
pub payment_data_filled_count: Option<u64>,
|
||||||
|
pub three_ds_method_invoked_count: Option<u64>,
|
||||||
|
pub three_ds_method_skipped_count: Option<u64>,
|
||||||
|
pub three_ds_method_successful_count: Option<u64>,
|
||||||
|
pub three_ds_method_unsuccessful_count: Option<u64>,
|
||||||
|
pub authentication_unsuccessful_count: Option<u64>,
|
||||||
|
pub three_ds_challenge_flow_count: Option<u64>,
|
||||||
|
pub three_ds_frictionless_flow_count: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
|||||||
Reference in New Issue
Block a user