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

View File

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

View File

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

View File

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

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()?;
query_builder
.add_filter_clause("event_name", SdkEventNames::StripeElementsCalled)
.add_filter_clause("event_name", SdkEventNames::OrcaElementsCalled)
.switch()?;
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)]
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

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