diff --git a/crates/analytics/src/auth_events/accumulator.rs b/crates/analytics/src/auth_events/accumulator.rs index 409e805af8..2958030c8d 100644 --- a/crates/analytics/src/auth_events/accumulator.rs +++ b/crates/analytics/src/auth_events/accumulator.rs @@ -11,6 +11,7 @@ pub struct AuthEventMetricsAccumulator { pub challenge_attempt_count: CountAccumulator, pub challenge_success_count: CountAccumulator, pub frictionless_flow_count: CountAccumulator, + pub frictionless_success_count: CountAccumulator, } #[derive(Debug, Default)] @@ -53,6 +54,7 @@ impl AuthEventMetricsAccumulator { challenge_attempt_count: self.challenge_attempt_count.collect(), challenge_success_count: self.challenge_success_count.collect(), frictionless_flow_count: self.frictionless_flow_count.collect(), + frictionless_success_count: self.frictionless_success_count.collect(), } } } diff --git a/crates/analytics/src/auth_events/core.rs b/crates/analytics/src/auth_events/core.rs index 761a95bb9b..5ee34d7a36 100644 --- a/crates/analytics/src/auth_events/core.rs +++ b/crates/analytics/src/auth_events/core.rs @@ -78,6 +78,9 @@ pub async fn get_metrics( AuthEventMetrics::FrictionlessFlowCount => metrics_builder .frictionless_flow_count .add_metrics_bucket(&value), + AuthEventMetrics::FrictionlessSuccessCount => metrics_builder + .frictionless_success_count + .add_metrics_bucket(&value), } } } diff --git a/crates/analytics/src/auth_events/metrics.rs b/crates/analytics/src/auth_events/metrics.rs index 6830747492..ae61aefb85 100644 --- a/crates/analytics/src/auth_events/metrics.rs +++ b/crates/analytics/src/auth_events/metrics.rs @@ -15,6 +15,7 @@ mod challenge_attempt_count; mod challenge_flow_count; mod challenge_success_count; mod frictionless_flow_count; +mod frictionless_success_count; mod three_ds_sdk_count; use authentication_attempt_count::AuthenticationAttemptCount; @@ -23,6 +24,7 @@ use challenge_attempt_count::ChallengeAttemptCount; use challenge_flow_count::ChallengeFlowCount; use challenge_success_count::ChallengeSuccessCount; use frictionless_flow_count::FrictionlessFlowCount; +use frictionless_success_count::FrictionlessSuccessCount; use three_ds_sdk_count::ThreeDsSdkCount; #[derive(Debug, PartialEq, Eq, serde::Deserialize)] @@ -102,6 +104,11 @@ where .load_metrics(merchant_id, publishable_key, granularity, time_range, pool) .await } + Self::FrictionlessSuccessCount => { + FrictionlessSuccessCount + .load_metrics(merchant_id, publishable_key, granularity, time_range, pool) + .await + } } } } diff --git a/crates/analytics/src/auth_events/metrics/challenge_attempt_count.rs b/crates/analytics/src/auth_events/metrics/challenge_attempt_count.rs index dbea5a1dc1..f2d2ab153f 100644 --- a/crates/analytics/src/auth_events/metrics/challenge_attempt_count.rs +++ b/crates/analytics/src/auth_events/metrics/challenge_attempt_count.rs @@ -8,7 +8,7 @@ use time::PrimitiveDateTime; use super::AuthEventMetricRow; use crate::{ - query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window}, + query::{Aggregate, FilterTypes, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window}, types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult}, }; @@ -34,7 +34,7 @@ where pool: &T, ) -> MetricsResult> { let mut query_builder: QueryBuilder = - QueryBuilder::new(AnalyticsCollection::ConnectorEventsAnalytics); + QueryBuilder::new(AnalyticsCollection::ApiEventsAnalytics); query_builder .add_select_column(Aggregate::Count { @@ -54,7 +54,11 @@ where .switch()?; query_builder - .add_filter_clause("flow", AuthEventFlows::PostAuthentication) + .add_filter_clause("api_flow", AuthEventFlows::IncomingWebhookReceive) + .switch()?; + + query_builder + .add_custom_filter_clause("request", "threeDSServerTransID", FilterTypes::Like) .switch()?; time_range diff --git a/crates/analytics/src/auth_events/metrics/challenge_success_count.rs b/crates/analytics/src/auth_events/metrics/challenge_success_count.rs index cb0932c33c..b7d55714b6 100644 --- a/crates/analytics/src/auth_events/metrics/challenge_success_count.rs +++ b/crates/analytics/src/auth_events/metrics/challenge_success_count.rs @@ -34,7 +34,7 @@ where pool: &T, ) -> MetricsResult> { let mut query_builder: QueryBuilder = - QueryBuilder::new(AnalyticsCollection::ConnectorEventsAnalytics); + QueryBuilder::new(AnalyticsCollection::ApiEventsAnalytics); query_builder .add_select_column(Aggregate::Count { @@ -54,11 +54,11 @@ where .switch()?; query_builder - .add_filter_clause("flow", AuthEventFlows::PostAuthentication) + .add_filter_clause("api_flow", AuthEventFlows::IncomingWebhookReceive) .switch()?; query_builder - .add_filter_clause("visitParamExtractRaw(response, 'transStatus')", "\"Y\"") + .add_filter_clause("visitParamExtractRaw(request, 'transStatus')", "\"Y\"") .switch()?; time_range diff --git a/crates/analytics/src/auth_events/metrics/frictionless_success_count.rs b/crates/analytics/src/auth_events/metrics/frictionless_success_count.rs new file mode 100644 index 0000000000..2f05a050ce --- /dev/null +++ b/crates/analytics/src/auth_events/metrics/frictionless_success_count.rs @@ -0,0 +1,94 @@ +use api_models::analytics::{ + auth_events::{AuthEventFlows, AuthEventMetricsBucketIdentifier}, + Granularity, TimeRange, +}; +use common_utils::errors::ReportSwitchExt; +use error_stack::ResultExt; +use time::PrimitiveDateTime; + +use super::AuthEventMetricRow; +use crate::{ + query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window}, + types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult}, +}; + +#[derive(Default)] +pub(super) struct FrictionlessSuccessCount; + +#[async_trait::async_trait] +impl super::AuthEventMetric for FrictionlessSuccessCount +where + T: AnalyticsDataSource + super::AuthEventMetricAnalytics, + PrimitiveDateTime: ToSql, + AnalyticsCollection: ToSql, + Granularity: GroupByClause, + Aggregate<&'static str>: ToSql, + Window<&'static str>: ToSql, +{ + async fn load_metrics( + &self, + merchant_id: &str, + _publishable_key: &str, + granularity: &Option, + time_range: &TimeRange, + pool: &T, + ) -> MetricsResult> { + let mut query_builder: QueryBuilder = + QueryBuilder::new(AnalyticsCollection::ApiEventsAnalytics); + + 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()?; + } + + query_builder + .add_filter_clause("merchant_id", merchant_id) + .switch()?; + + query_builder + .add_filter_clause("api_flow", AuthEventFlows::PaymentsExternalAuthentication) + .switch()?; + + query_builder + .add_filter_clause("visitParamExtractRaw(response, 'transStatus')", "\"Y\"") + .switch()?; + + time_range + .set_filter_clause(&mut query_builder) + .attach_printable("Error filtering time range") + .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::(pool) + .await + .change_context(MetricsError::QueryBuildingError)? + .change_context(MetricsError::QueryExecutionFailure)? + .into_iter() + .map(|i| { + Ok(( + AuthEventMetricsBucketIdentifier::new(i.time_bucket.clone()), + i, + )) + }) + .collect::, + crate::query::PostProcessingError, + >>() + .change_context(MetricsError::PostProcessingFailure) + } +} diff --git a/crates/analytics/src/clickhouse.rs b/crates/analytics/src/clickhouse.rs index 3009c036b9..64064c09c8 100644 --- a/crates/analytics/src/clickhouse.rs +++ b/crates/analytics/src/clickhouse.rs @@ -136,7 +136,7 @@ impl AnalyticsDataSource for ClickhouseClient { AnalyticsCollection::SdkEvents | AnalyticsCollection::ApiEvents | AnalyticsCollection::ConnectorEvents - | AnalyticsCollection::ConnectorEventsAnalytics + | AnalyticsCollection::ApiEventsAnalytics | AnalyticsCollection::OutgoingWebhookEvent => TableEngine::BasicTree, } } @@ -374,7 +374,7 @@ impl ToSql for AnalyticsCollection { Self::Refund => Ok("refunds".to_string()), Self::SdkEvents => Ok("sdk_events_audit".to_string()), Self::ApiEvents => Ok("api_events_audit".to_string()), - Self::ConnectorEventsAnalytics => Ok("connector_events".to_string()), + Self::ApiEventsAnalytics => Ok("api_events".to_string()), Self::PaymentIntent => Ok("payment_intents".to_string()), Self::ConnectorEvents => Ok("connector_events_audit".to_string()), Self::OutgoingWebhookEvent => Ok("outgoing_webhook_events_audit".to_string()), diff --git a/crates/analytics/src/sqlx.rs b/crates/analytics/src/sqlx.rs index 909171b222..d3cb24a00c 100644 --- a/crates/analytics/src/sqlx.rs +++ b/crates/analytics/src/sqlx.rs @@ -548,10 +548,10 @@ impl ToSql for AnalyticsCollection { Self::ApiEvents => Err(error_stack::report!(ParsingError::UnknownError) .attach_printable("ApiEvents table is not implemented for Sqlx"))?, Self::PaymentIntent => Ok("payment_intent".to_string()), - Self::ConnectorEvents | Self::ConnectorEventsAnalytics => { - Err(error_stack::report!(ParsingError::UnknownError) - .attach_printable("ConnectorEvents table is not implemented for Sqlx"))? - } + Self::ConnectorEvents => Err(error_stack::report!(ParsingError::UnknownError) + .attach_printable("ConnectorEvents table is not implemented for Sqlx"))?, + Self::ApiEventsAnalytics => Err(error_stack::report!(ParsingError::UnknownError) + .attach_printable("ApiEvents table is not implemented for Sqlx"))?, Self::OutgoingWebhookEvent => Err(error_stack::report!(ParsingError::UnknownError) .attach_printable("OutgoingWebhookEvents table is not implemented for Sqlx"))?, Self::Dispute => Ok("dispute".to_string()), diff --git a/crates/analytics/src/types.rs b/crates/analytics/src/types.rs index e8196a0968..86a9ec86ef 100644 --- a/crates/analytics/src/types.rs +++ b/crates/analytics/src/types.rs @@ -31,7 +31,7 @@ pub enum AnalyticsCollection { ConnectorEvents, OutgoingWebhookEvent, Dispute, - ConnectorEventsAnalytics, + ApiEventsAnalytics, } #[allow(dead_code)] diff --git a/crates/api_models/src/analytics/auth_events.rs b/crates/api_models/src/analytics/auth_events.rs index bb08f9f593..7791a2f3cd 100644 --- a/crates/api_models/src/analytics/auth_events.rs +++ b/crates/api_models/src/analytics/auth_events.rs @@ -25,6 +25,7 @@ pub enum AuthEventMetrics { AuthenticationSuccessCount, ChallengeFlowCount, FrictionlessFlowCount, + FrictionlessSuccessCount, ChallengeAttemptCount, ChallengeSuccessCount, } @@ -42,7 +43,8 @@ pub enum AuthEventMetrics { strum::AsRefStr, )] pub enum AuthEventFlows { - PostAuthentication, + IncomingWebhookReceive, + PaymentsExternalAuthentication, } pub mod metric_behaviour { @@ -51,6 +53,7 @@ pub mod metric_behaviour { pub struct AuthenticationSuccessCount; pub struct ChallengeFlowCount; pub struct FrictionlessFlowCount; + pub struct FrictionlessSuccessCount; pub struct ChallengeAttemptCount; pub struct ChallengeSuccessCount; } @@ -100,6 +103,7 @@ pub struct AuthEventMetricsBucketValue { pub challenge_attempt_count: Option, pub challenge_success_count: Option, pub frictionless_flow_count: Option, + pub frictionless_success_count: Option, } #[derive(Debug, serde::Serialize)]