mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +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)]
|
||||
pub enum FilterTypes {
|
||||
Equal,
|
||||
NotEqual,
|
||||
EqualBool,
|
||||
In,
|
||||
Gte,
|
||||
@ -402,6 +403,7 @@ pub fn filter_type_to_sql(l: &String, op: &FilterTypes, r: &String) -> String {
|
||||
match op {
|
||||
FilterTypes::EqualBool => format!("{l} = {r}"),
|
||||
FilterTypes::Equal => format!("{l} = '{r}'"),
|
||||
FilterTypes::NotEqual => format!("{l} != '{r}'"),
|
||||
FilterTypes::In => format!("{l} IN ({r})"),
|
||||
FilterTypes::Gte => format!("{l} >= '{r}'"),
|
||||
FilterTypes::Gt => format!("{l} > {r}"),
|
||||
|
||||
@ -6,13 +6,19 @@ use super::metrics::SdkEventMetricRow;
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SdkEventMetricsAccumulator {
|
||||
pub payment_attempts: CountAccumulator,
|
||||
pub payment_success: CountAccumulator,
|
||||
pub payment_methods_call_count: CountAccumulator,
|
||||
pub average_payment_time: AverageAccumulator,
|
||||
pub sdk_initiated_count: CountAccumulator,
|
||||
pub sdk_rendered_count: CountAccumulator,
|
||||
pub payment_method_selected_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)]
|
||||
@ -86,13 +92,19 @@ impl SdkEventMetricsAccumulator {
|
||||
pub fn collect(self) -> SdkEventMetricsBucketValue {
|
||||
SdkEventMetricsBucketValue {
|
||||
payment_attempts: self.payment_attempts.collect(),
|
||||
payment_success_count: self.payment_success.collect(),
|
||||
payment_methods_call_count: self.payment_methods_call_count.collect(),
|
||||
average_payment_time: self.average_payment_time.collect(),
|
||||
sdk_initiated_count: self.sdk_initiated_count.collect(),
|
||||
sdk_rendered_count: self.sdk_rendered_count.collect(),
|
||||
payment_method_selected_count: self.payment_method_selected_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 => {
|
||||
metrics_builder.payment_attempts.add_metrics_bucket(&value)
|
||||
}
|
||||
SdkEventMetrics::PaymentSuccessCount => {
|
||||
metrics_builder.payment_success.add_metrics_bucket(&value)
|
||||
}
|
||||
SdkEventMetrics::PaymentMethodsCallCount => metrics_builder
|
||||
.payment_methods_call_count
|
||||
.add_metrics_bucket(&value),
|
||||
@ -109,6 +106,27 @@ pub async fn get_metrics(
|
||||
SdkEventMetrics::AveragePaymentTime => metrics_builder
|
||||
.average_payment_time
|
||||
.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},
|
||||
};
|
||||
|
||||
mod authentication_unsuccessful_count;
|
||||
mod average_payment_time;
|
||||
mod payment_attempts;
|
||||
mod payment_data_filled_count;
|
||||
mod payment_method_selected_count;
|
||||
mod payment_methods_call_count;
|
||||
mod payment_success_count;
|
||||
mod sdk_initiated_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 payment_attempts::PaymentAttempts;
|
||||
use payment_data_filled_count::PaymentDataFilledCount;
|
||||
use payment_method_selected_count::PaymentMethodSelectedCount;
|
||||
use payment_methods_call_count::PaymentMethodsCallCount;
|
||||
use payment_success_count::PaymentSuccessCount;
|
||||
use sdk_initiated_count::SdkInitiatedCount;
|
||||
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)]
|
||||
pub struct SdkEventMetricRow {
|
||||
@ -92,18 +104,6 @@ where
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::PaymentSuccessCount => {
|
||||
PaymentSuccessCount
|
||||
.load_metrics(
|
||||
dimensions,
|
||||
publishable_key,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Self::PaymentMethodsCallCount => {
|
||||
PaymentMethodsCallCount
|
||||
.load_metrics(
|
||||
@ -176,6 +176,90 @@ where
|
||||
)
|
||||
.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()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("event_name", SdkEventNames::StripeElementsCalled)
|
||||
.add_filter_clause("event_name", SdkEventNames::OrcaElementsCalled)
|
||||
.switch()?;
|
||||
|
||||
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)]
|
||||
pub(super) struct PaymentSuccessCount;
|
||||
pub(super) struct ThreeDsMethodSuccessfulCount;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<T> super::SdkEventMetric<T> for PaymentSuccessCount
|
||||
impl<T> super::SdkEventMetric<T> for ThreeDsMethodSuccessfulCount
|
||||
where
|
||||
T: AnalyticsDataSource + super::SdkEventMetricAnalytics,
|
||||
PrimitiveDateTime: ToSql<T>,
|
||||
@ -63,11 +63,15 @@ where
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_bool_filter_clause("first_event", 1)
|
||||
.add_filter_clause("event_name", SdkEventNames::ThreeDsMethodResult)
|
||||
.switch()?;
|
||||
|
||||
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()?;
|
||||
|
||||
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")]
|
||||
pub enum SdkEventMetrics {
|
||||
PaymentAttempts,
|
||||
PaymentSuccessCount,
|
||||
PaymentMethodsCallCount,
|
||||
ThreeDsMethodInvokedCount,
|
||||
ThreeDsMethodSkippedCount,
|
||||
ThreeDsMethodSuccessfulCount,
|
||||
ThreeDsMethodUnsuccessfulCount,
|
||||
AuthenticationUnsuccessfulCount,
|
||||
ThreeDsChallengeFlowCount,
|
||||
ThreeDsFrictionlessFlowCount,
|
||||
SdkRenderedCount,
|
||||
SdkInitiatedCount,
|
||||
PaymentMethodSelectedCount,
|
||||
@ -95,12 +101,11 @@ pub enum SdkEventMetrics {
|
||||
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum SdkEventNames {
|
||||
StripeElementsCalled,
|
||||
OrcaElementsCalled,
|
||||
AppRendered,
|
||||
PaymentMethodChanged,
|
||||
PaymentDataFilled,
|
||||
PaymentAttempt,
|
||||
PaymentSuccess,
|
||||
PaymentMethodsCall,
|
||||
ConfirmCall,
|
||||
SessionsCall,
|
||||
@ -108,12 +113,24 @@ pub enum SdkEventNames {
|
||||
RedirectingUser,
|
||||
DisplayBankTransferInfoPage,
|
||||
DisplayQrCodeInfoPage,
|
||||
AuthenticationCall,
|
||||
ThreeDsMethodCall,
|
||||
ThreeDsMethodResult,
|
||||
ThreeDsMethod,
|
||||
LoaderChanged,
|
||||
DisplayThreeDsSdk,
|
||||
}
|
||||
|
||||
pub mod metric_behaviour {
|
||||
pub struct PaymentAttempts;
|
||||
pub struct PaymentSuccessCount;
|
||||
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 SdkInitiatedCount;
|
||||
pub struct PaymentMethodSelectedCount;
|
||||
@ -197,13 +214,19 @@ impl PartialEq for SdkEventMetricsBucketIdentifier {
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct SdkEventMetricsBucketValue {
|
||||
pub payment_attempts: Option<u64>,
|
||||
pub payment_success_count: Option<u64>,
|
||||
pub payment_methods_call_count: Option<u64>,
|
||||
pub average_payment_time: Option<f64>,
|
||||
pub sdk_rendered_count: Option<u64>,
|
||||
pub sdk_initiated_count: Option<u64>,
|
||||
pub payment_method_selected_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)]
|
||||
|
||||
Reference in New Issue
Block a user