mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
feat(analytics): refactor and rewrite authentication related analytics (#7433)
Co-authored-by: Sandeep Kumar <sandeep.kumar@Sandeep-Kumar-LVF93XQXPC.local>
This commit is contained in:
@ -4,7 +4,7 @@ use super::metrics::AuthEventMetricRow;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AuthEventMetricsAccumulator {
|
||||
pub three_ds_sdk_count: CountAccumulator,
|
||||
pub authentication_count: CountAccumulator,
|
||||
pub authentication_attempt_count: CountAccumulator,
|
||||
pub authentication_success_count: CountAccumulator,
|
||||
pub challenge_flow_count: CountAccumulator,
|
||||
@ -47,7 +47,7 @@ impl AuthEventMetricAccumulator for CountAccumulator {
|
||||
impl AuthEventMetricsAccumulator {
|
||||
pub fn collect(self) -> AuthEventMetricsBucketValue {
|
||||
AuthEventMetricsBucketValue {
|
||||
three_ds_sdk_count: self.three_ds_sdk_count.collect(),
|
||||
authentication_count: self.authentication_count.collect(),
|
||||
authentication_attempt_count: self.authentication_attempt_count.collect(),
|
||||
authentication_success_count: self.authentication_success_count.collect(),
|
||||
challenge_flow_count: self.challenge_flow_count.collect(),
|
||||
|
||||
@ -18,7 +18,6 @@ use crate::{
|
||||
pub async fn get_metrics(
|
||||
pool: &AnalyticsProvider,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &String,
|
||||
req: GetAuthEventMetricRequest,
|
||||
) -> AnalyticsResult<MetricsResponse<MetricsBucketResponse>> {
|
||||
let mut metrics_accumulator: HashMap<
|
||||
@ -30,14 +29,12 @@ pub async fn get_metrics(
|
||||
for metric_type in req.metrics.iter().cloned() {
|
||||
let req = req.clone();
|
||||
let merchant_id_scoped = merchant_id.to_owned();
|
||||
let publishable_key_scoped = publishable_key.to_owned();
|
||||
let pool = pool.clone();
|
||||
set.spawn(async move {
|
||||
let data = pool
|
||||
.get_auth_event_metrics(
|
||||
&metric_type,
|
||||
&merchant_id_scoped,
|
||||
&publishable_key_scoped,
|
||||
req.time_series.map(|t| t.granularity),
|
||||
&req.time_range,
|
||||
)
|
||||
@ -56,8 +53,8 @@ pub async fn get_metrics(
|
||||
for (id, value) in data? {
|
||||
let metrics_builder = metrics_accumulator.entry(id).or_default();
|
||||
match metric {
|
||||
AuthEventMetrics::ThreeDsSdkCount => metrics_builder
|
||||
.three_ds_sdk_count
|
||||
AuthEventMetrics::AuthenticationCount => metrics_builder
|
||||
.authentication_count
|
||||
.add_metrics_bucket(&value),
|
||||
AuthEventMetrics::AuthenticationAttemptCount => metrics_builder
|
||||
.authentication_attempt_count
|
||||
|
||||
@ -12,22 +12,22 @@ use crate::{
|
||||
};
|
||||
|
||||
mod authentication_attempt_count;
|
||||
mod authentication_count;
|
||||
mod authentication_success_count;
|
||||
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;
|
||||
use authentication_count::AuthenticationCount;
|
||||
use authentication_success_count::AuthenticationSuccessCount;
|
||||
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, Hash)]
|
||||
pub struct AuthEventMetricRow {
|
||||
@ -45,7 +45,6 @@ where
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &str,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
@ -65,50 +64,49 @@ where
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &str,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
match self {
|
||||
Self::ThreeDsSdkCount => {
|
||||
ThreeDsSdkCount
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
Self::AuthenticationCount => {
|
||||
AuthenticationCount
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::AuthenticationAttemptCount => {
|
||||
AuthenticationAttemptCount
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::AuthenticationSuccessCount => {
|
||||
AuthenticationSuccessCount
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::ChallengeFlowCount => {
|
||||
ChallengeFlowCount
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::ChallengeAttemptCount => {
|
||||
ChallengeAttemptCount
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::ChallengeSuccessCount => {
|
||||
ChallengeSuccessCount
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::FrictionlessFlowCount => {
|
||||
FrictionlessFlowCount
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::FrictionlessSuccessCount => {
|
||||
FrictionlessSuccessCount
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use api_models::analytics::{
|
||||
auth_events::AuthEventMetricsBucketIdentifier, sdk_events::SdkEventNames, Granularity,
|
||||
TimeRange,
|
||||
auth_events::AuthEventMetricsBucketIdentifier, Granularity, TimeRange,
|
||||
};
|
||||
use common_enums::AuthenticationStatus;
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::ResultExt;
|
||||
use time::PrimitiveDateTime;
|
||||
@ -29,14 +29,13 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
_merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &str,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
let mut query_builder: QueryBuilder<T> =
|
||||
QueryBuilder::new(AnalyticsCollection::SdkEventsAnalytics);
|
||||
QueryBuilder::new(AnalyticsCollection::Authentications);
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Count {
|
||||
@ -45,30 +44,26 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
if let Some(granularity) = granularity {
|
||||
query_builder
|
||||
.add_granularity_in_mins(granularity)
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", publishable_key)
|
||||
.add_select_column(Aggregate::Min {
|
||||
field: "created_at",
|
||||
alias: Some("start_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_bool_filter_clause("first_event", 1)
|
||||
.add_select_column(Aggregate::Max {
|
||||
field: "created_at",
|
||||
alias: Some("end_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("event_name", SdkEventNames::AuthenticationCallInit)
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("log_type", "INFO")
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("category", "API")
|
||||
.add_negative_filter_clause("authentication_status", AuthenticationStatus::Pending)
|
||||
.switch()?;
|
||||
|
||||
time_range
|
||||
@ -76,9 +71,9 @@ where
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
|
||||
if let Some(_granularity) = granularity.as_ref() {
|
||||
query_builder
|
||||
.add_group_by_clause("time_bucket")
|
||||
if let Some(granularity) = granularity {
|
||||
granularity
|
||||
.set_group_by_clause(&mut query_builder)
|
||||
.attach_printable("Error adding granularity")
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use api_models::analytics::{
|
||||
auth_events::AuthEventMetricsBucketIdentifier, sdk_events::SdkEventNames, Granularity,
|
||||
TimeRange,
|
||||
auth_events::AuthEventMetricsBucketIdentifier, Granularity, TimeRange,
|
||||
};
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::ResultExt;
|
||||
@ -15,10 +14,10 @@ use crate::{
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct ThreeDsSdkCount;
|
||||
pub(super) struct AuthenticationCount;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<T> super::AuthEventMetric<T> for ThreeDsSdkCount
|
||||
impl<T> super::AuthEventMetric<T> for AuthenticationCount
|
||||
where
|
||||
T: AnalyticsDataSource + super::AuthEventMetricAnalytics,
|
||||
PrimitiveDateTime: ToSql<T>,
|
||||
@ -29,14 +28,13 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
_merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &str,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
let mut query_builder: QueryBuilder<T> =
|
||||
QueryBuilder::new(AnalyticsCollection::SdkEventsAnalytics);
|
||||
QueryBuilder::new(AnalyticsCollection::Authentications);
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Count {
|
||||
@ -45,30 +43,22 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
if let Some(granularity) = granularity {
|
||||
query_builder
|
||||
.add_granularity_in_mins(granularity)
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", publishable_key)
|
||||
.add_select_column(Aggregate::Min {
|
||||
field: "created_at",
|
||||
alias: Some("start_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_bool_filter_clause("first_event", 1)
|
||||
.add_select_column(Aggregate::Max {
|
||||
field: "created_at",
|
||||
alias: Some("end_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("category", "USER_EVENT")
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("log_type", "INFO")
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("event_name", SdkEventNames::ThreeDsMethod)
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
time_range
|
||||
@ -76,9 +66,9 @@ where
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
|
||||
if let Some(_granularity) = granularity.as_ref() {
|
||||
query_builder
|
||||
.add_group_by_clause("time_bucket")
|
||||
if let Some(granularity) = granularity {
|
||||
granularity
|
||||
.set_group_by_clause(&mut query_builder)
|
||||
.attach_printable("Error adding granularity")
|
||||
.switch()?;
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use api_models::analytics::{
|
||||
auth_events::AuthEventMetricsBucketIdentifier, sdk_events::SdkEventNames, Granularity,
|
||||
TimeRange,
|
||||
auth_events::AuthEventMetricsBucketIdentifier, Granularity, TimeRange,
|
||||
};
|
||||
use common_enums::AuthenticationStatus;
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::ResultExt;
|
||||
use time::PrimitiveDateTime;
|
||||
@ -29,14 +29,13 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
_merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &str,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
let mut query_builder: QueryBuilder<T> =
|
||||
QueryBuilder::new(AnalyticsCollection::SdkEventsAnalytics);
|
||||
QueryBuilder::new(AnalyticsCollection::Authentications);
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Count {
|
||||
@ -45,30 +44,26 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
if let Some(granularity) = granularity {
|
||||
query_builder
|
||||
.add_granularity_in_mins(granularity)
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", publishable_key)
|
||||
.add_select_column(Aggregate::Min {
|
||||
field: "created_at",
|
||||
alias: Some("start_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_bool_filter_clause("first_event", 1)
|
||||
.add_select_column(Aggregate::Max {
|
||||
field: "created_at",
|
||||
alias: Some("end_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("event_name", SdkEventNames::AuthenticationCall)
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("log_type", "INFO")
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("category", "API")
|
||||
.add_filter_clause("authentication_status", AuthenticationStatus::Success)
|
||||
.switch()?;
|
||||
|
||||
time_range
|
||||
@ -76,9 +71,9 @@ where
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
|
||||
if let Some(_granularity) = granularity.as_ref() {
|
||||
query_builder
|
||||
.add_group_by_clause("time_bucket")
|
||||
if let Some(granularity) = granularity {
|
||||
granularity
|
||||
.set_group_by_clause(&mut query_builder)
|
||||
.attach_printable("Error adding granularity")
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use api_models::analytics::{
|
||||
auth_events::{AuthEventFlows, AuthEventMetricsBucketIdentifier},
|
||||
Granularity, TimeRange,
|
||||
auth_events::AuthEventMetricsBucketIdentifier, Granularity, TimeRange,
|
||||
};
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::ResultExt;
|
||||
@ -10,7 +9,7 @@ use time::PrimitiveDateTime;
|
||||
|
||||
use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, FilterTypes, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
};
|
||||
|
||||
@ -30,13 +29,12 @@ where
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
_publishable_key: &str,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
let mut query_builder: QueryBuilder<T> =
|
||||
QueryBuilder::new(AnalyticsCollection::ApiEventsAnalytics);
|
||||
QueryBuilder::new(AnalyticsCollection::Authentications);
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Count {
|
||||
@ -45,22 +43,30 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
if let Some(granularity) = granularity {
|
||||
query_builder
|
||||
.add_granularity_in_mins(granularity)
|
||||
.add_select_column(Aggregate::Min {
|
||||
field: "created_at",
|
||||
alias: Some("start_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Max {
|
||||
field: "created_at",
|
||||
alias: Some("end_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("api_flow", AuthEventFlows::IncomingWebhookReceive)
|
||||
.add_filter_clause("trans_status", "C".to_string())
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_custom_filter_clause("request", "threeDSServerTransID", FilterTypes::Like)
|
||||
.add_negative_filter_clause("authentication_status", "pending")
|
||||
.switch()?;
|
||||
|
||||
time_range
|
||||
@ -68,9 +74,9 @@ where
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
|
||||
if let Some(_granularity) = granularity.as_ref() {
|
||||
query_builder
|
||||
.add_group_by_clause("time_bucket")
|
||||
if let Some(granularity) = granularity {
|
||||
granularity
|
||||
.set_group_by_clause(&mut query_builder)
|
||||
.attach_printable("Error adding granularity")
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use api_models::analytics::{
|
||||
auth_events::AuthEventMetricsBucketIdentifier, sdk_events::SdkEventNames, Granularity,
|
||||
TimeRange,
|
||||
auth_events::AuthEventMetricsBucketIdentifier, Granularity, TimeRange,
|
||||
};
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::ResultExt;
|
||||
@ -29,14 +28,13 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
_merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &str,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
let mut query_builder: QueryBuilder<T> =
|
||||
QueryBuilder::new(AnalyticsCollection::SdkEventsAnalytics);
|
||||
QueryBuilder::new(AnalyticsCollection::Authentications);
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Count {
|
||||
@ -45,42 +43,36 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
if let Some(granularity) = granularity {
|
||||
query_builder
|
||||
.add_granularity_in_mins(granularity)
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", publishable_key)
|
||||
.add_select_column(Aggregate::Min {
|
||||
field: "created_at",
|
||||
alias: Some("start_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_bool_filter_clause("first_event", 1)
|
||||
.add_select_column(Aggregate::Max {
|
||||
field: "created_at",
|
||||
alias: Some("end_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("category", "USER_EVENT")
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("log_type", "INFO")
|
||||
.add_filter_clause("trans_status", "C".to_string())
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("event_name", SdkEventNames::DisplayThreeDsSdk)
|
||||
.switch()?;
|
||||
|
||||
query_builder.add_filter_clause("value", "C").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")
|
||||
if let Some(granularity) = granularity {
|
||||
granularity
|
||||
.set_group_by_clause(&mut query_builder)
|
||||
.attach_printable("Error adding granularity")
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use api_models::analytics::{
|
||||
auth_events::{AuthEventFlows, AuthEventMetricsBucketIdentifier},
|
||||
Granularity, TimeRange,
|
||||
auth_events::AuthEventMetricsBucketIdentifier, Granularity, TimeRange,
|
||||
};
|
||||
use common_enums::AuthenticationStatus;
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::ResultExt;
|
||||
use time::PrimitiveDateTime;
|
||||
@ -30,13 +30,12 @@ where
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
_publishable_key: &str,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
let mut query_builder: QueryBuilder<T> =
|
||||
QueryBuilder::new(AnalyticsCollection::ApiEventsAnalytics);
|
||||
QueryBuilder::new(AnalyticsCollection::Authentications);
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Count {
|
||||
@ -45,22 +44,30 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
if let Some(granularity) = granularity {
|
||||
query_builder
|
||||
.add_granularity_in_mins(granularity)
|
||||
.add_select_column(Aggregate::Min {
|
||||
field: "created_at",
|
||||
alias: Some("start_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Max {
|
||||
field: "created_at",
|
||||
alias: Some("end_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("api_flow", AuthEventFlows::IncomingWebhookReceive)
|
||||
.add_filter_clause("authentication_status", AuthenticationStatus::Success)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("visitParamExtractRaw(request, 'transStatus')", "\"Y\"")
|
||||
.add_filter_clause("trans_status", "C".to_string())
|
||||
.switch()?;
|
||||
|
||||
time_range
|
||||
@ -68,9 +75,9 @@ where
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
|
||||
if let Some(_granularity) = granularity.as_ref() {
|
||||
query_builder
|
||||
.add_group_by_clause("time_bucket")
|
||||
if let Some(granularity) = granularity {
|
||||
granularity
|
||||
.set_group_by_clause(&mut query_builder)
|
||||
.attach_printable("Error adding granularity")
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use api_models::analytics::{
|
||||
auth_events::AuthEventMetricsBucketIdentifier, sdk_events::SdkEventNames, Granularity,
|
||||
TimeRange,
|
||||
auth_events::AuthEventMetricsBucketIdentifier, Granularity, TimeRange,
|
||||
};
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::ResultExt;
|
||||
@ -29,14 +28,13 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
_merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &str,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
let mut query_builder: QueryBuilder<T> =
|
||||
QueryBuilder::new(AnalyticsCollection::SdkEventsAnalytics);
|
||||
QueryBuilder::new(AnalyticsCollection::Authentications);
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Count {
|
||||
@ -45,34 +43,26 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
if let Some(granularity) = granularity {
|
||||
query_builder
|
||||
.add_granularity_in_mins(granularity)
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", publishable_key)
|
||||
.add_select_column(Aggregate::Min {
|
||||
field: "created_at",
|
||||
alias: Some("start_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_bool_filter_clause("first_event", 1)
|
||||
.add_select_column(Aggregate::Max {
|
||||
field: "created_at",
|
||||
alias: Some("end_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("category", "USER_EVENT")
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("log_type", "INFO")
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("event_name", SdkEventNames::DisplayThreeDsSdk)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_negative_filter_clause("value", "C")
|
||||
.add_filter_clause("trans_status", "Y".to_string())
|
||||
.switch()?;
|
||||
|
||||
time_range
|
||||
@ -80,9 +70,9 @@ where
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
|
||||
if let Some(_granularity) = granularity.as_ref() {
|
||||
query_builder
|
||||
.add_group_by_clause("time_bucket")
|
||||
if let Some(granularity) = granularity {
|
||||
granularity
|
||||
.set_group_by_clause(&mut query_builder)
|
||||
.attach_printable("Error adding granularity")
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use api_models::analytics::{
|
||||
auth_events::{AuthEventFlows, AuthEventMetricsBucketIdentifier},
|
||||
Granularity, TimeRange,
|
||||
auth_events::AuthEventMetricsBucketIdentifier, Granularity, TimeRange,
|
||||
};
|
||||
use common_enums::AuthenticationStatus;
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::ResultExt;
|
||||
use time::PrimitiveDateTime;
|
||||
@ -30,13 +30,12 @@ where
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
_publishable_key: &str,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
let mut query_builder: QueryBuilder<T> =
|
||||
QueryBuilder::new(AnalyticsCollection::ApiEventsAnalytics);
|
||||
QueryBuilder::new(AnalyticsCollection::Authentications);
|
||||
|
||||
query_builder
|
||||
.add_select_column(Aggregate::Count {
|
||||
@ -45,22 +44,30 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
if let Some(granularity) = granularity {
|
||||
query_builder
|
||||
.add_granularity_in_mins(granularity)
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id.get_string_repr())
|
||||
.add_select_column(Aggregate::Min {
|
||||
field: "created_at",
|
||||
alias: Some("start_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("api_flow", AuthEventFlows::PaymentsExternalAuthentication)
|
||||
.add_select_column(Aggregate::Max {
|
||||
field: "created_at",
|
||||
alias: Some("end_bucket"),
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("visitParamExtractRaw(response, 'transStatus')", "\"Y\"")
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("trans_status", "Y".to_string())
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("authentication_status", AuthenticationStatus::Success)
|
||||
.switch()?;
|
||||
|
||||
time_range
|
||||
@ -68,9 +75,9 @@ where
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
|
||||
if let Some(_granularity) = granularity.as_ref() {
|
||||
query_builder
|
||||
.add_group_by_clause("time_bucket")
|
||||
if let Some(granularity) = granularity {
|
||||
granularity
|
||||
.set_group_by_clause(&mut query_builder)
|
||||
.attach_printable("Error adding granularity")
|
||||
.switch()?;
|
||||
}
|
||||
|
||||
@ -138,6 +138,7 @@ impl AnalyticsDataSource for ClickhouseClient {
|
||||
| AnalyticsCollection::FraudCheck
|
||||
| AnalyticsCollection::PaymentIntent
|
||||
| AnalyticsCollection::PaymentIntentSessionized
|
||||
| AnalyticsCollection::Authentications
|
||||
| AnalyticsCollection::Dispute => {
|
||||
TableEngine::CollapsingMergeTree { sign: "sign_flag" }
|
||||
}
|
||||
@ -457,6 +458,7 @@ impl ToSql<ClickhouseClient> for AnalyticsCollection {
|
||||
Self::Dispute => Ok("dispute".to_string()),
|
||||
Self::DisputeSessionized => Ok("sessionizer_dispute".to_string()),
|
||||
Self::ActivePaymentsAnalytics => Ok("active_payments".to_string()),
|
||||
Self::Authentications => Ok("authentications".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -909,7 +909,6 @@ impl AnalyticsProvider {
|
||||
&self,
|
||||
metric: &AuthEventMetrics,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
publishable_key: &str,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
) -> types::MetricsResult<HashSet<(AuthEventMetricsBucketIdentifier, AuthEventMetricRow)>> {
|
||||
@ -917,14 +916,13 @@ impl AnalyticsProvider {
|
||||
Self::Sqlx(_pool) => Err(report!(MetricsError::NotImplemented)),
|
||||
Self::Clickhouse(pool) => {
|
||||
metric
|
||||
.load_metrics(merchant_id, publishable_key, granularity, time_range, pool)
|
||||
.load_metrics(merchant_id, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::CombinedCkh(_sqlx_pool, ckh_pool) | Self::CombinedSqlx(_sqlx_pool, ckh_pool) => {
|
||||
metric
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
publishable_key,
|
||||
granularity,
|
||||
// Since API events are ckh only use ckh here
|
||||
time_range,
|
||||
|
||||
@ -19,6 +19,7 @@ use api_models::{
|
||||
},
|
||||
refunds::RefundStatus,
|
||||
};
|
||||
use common_enums::{AuthenticationStatus, TransactionStatus};
|
||||
use common_utils::{
|
||||
errors::{CustomResult, ParsingError},
|
||||
id_type::{MerchantId, OrganizationId, ProfileId},
|
||||
@ -502,6 +503,8 @@ impl_to_sql_for_to_string!(
|
||||
Currency,
|
||||
RefundType,
|
||||
FrmTransactionType,
|
||||
TransactionStatus,
|
||||
AuthenticationStatus,
|
||||
Flow,
|
||||
&String,
|
||||
&bool,
|
||||
|
||||
@ -1034,6 +1034,8 @@ impl ToSql<SqlxClient> for AnalyticsCollection {
|
||||
Self::Dispute => Ok("dispute".to_string()),
|
||||
Self::DisputeSessionized => Err(error_stack::report!(ParsingError::UnknownError)
|
||||
.attach_printable("DisputeSessionized table is not implemented for Sqlx"))?,
|
||||
Self::Authentications => Err(error_stack::report!(ParsingError::UnknownError)
|
||||
.attach_printable("Authentications table is not implemented for Sqlx"))?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ pub enum AnalyticsCollection {
|
||||
PaymentIntentSessionized,
|
||||
ConnectorEvents,
|
||||
OutgoingWebhookEvent,
|
||||
Authentications,
|
||||
Dispute,
|
||||
DisputeSessionized,
|
||||
ApiEventsAnalytics,
|
||||
|
||||
@ -5,6 +5,29 @@ use std::{
|
||||
|
||||
use super::NameDescription;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
serde::Serialize,
|
||||
serde::Deserialize,
|
||||
strum::AsRefStr,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Eq,
|
||||
Ord,
|
||||
strum::Display,
|
||||
strum::EnumIter,
|
||||
Clone,
|
||||
Copy,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum AuthEventDimensions {
|
||||
#[serde(rename = "authentication_status")]
|
||||
AuthenticationStatus,
|
||||
#[serde(rename = "trans_status")]
|
||||
TransactionStatus,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
@ -20,7 +43,7 @@ use super::NameDescription;
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AuthEventMetrics {
|
||||
ThreeDsSdkCount,
|
||||
AuthenticationCount,
|
||||
AuthenticationAttemptCount,
|
||||
AuthenticationSuccessCount,
|
||||
ChallengeFlowCount,
|
||||
@ -48,7 +71,7 @@ pub enum AuthEventFlows {
|
||||
}
|
||||
|
||||
pub mod metric_behaviour {
|
||||
pub struct ThreeDsSdkCount;
|
||||
pub struct AuthenticationCount;
|
||||
pub struct AuthenticationAttemptCount;
|
||||
pub struct AuthenticationSuccessCount;
|
||||
pub struct ChallengeFlowCount;
|
||||
@ -96,7 +119,7 @@ impl PartialEq for AuthEventMetricsBucketIdentifier {
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct AuthEventMetricsBucketValue {
|
||||
pub three_ds_sdk_count: Option<u64>,
|
||||
pub authentication_count: Option<u64>,
|
||||
pub authentication_attempt_count: Option<u64>,
|
||||
pub authentication_success_count: Option<u64>,
|
||||
pub challenge_flow_count: Option<u64>,
|
||||
|
||||
@ -975,7 +975,6 @@ pub mod routes {
|
||||
analytics::auth_events::get_metrics(
|
||||
&state.pool,
|
||||
auth.merchant_account.get_id(),
|
||||
&auth.merchant_account.publishable_key,
|
||||
req,
|
||||
)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user