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:
Vrishab Srivatsa
2024-04-02 17:39:31 +05:30
committed by GitHub
parent a071463b29
commit 88b53b0d5c
13 changed files with 914 additions and 29 deletions

View File

@ -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}"),

View File

@ -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(),
} }
} }
} }

View File

@ -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),
} }
} }

View File

@ -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
}
} }
} }
} }

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)]