mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-27 19:46:48 +08:00
refactor: exposed auth analytics at merchant,org and profile levels (#8335)
This commit is contained in:
@ -27,7 +27,7 @@ use crate::{
|
||||
#[instrument(skip_all)]
|
||||
pub async fn get_metrics(
|
||||
pool: &AnalyticsProvider,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
req: GetAuthEventMetricRequest,
|
||||
) -> AnalyticsResult<AuthEventMetricsResponse<MetricsBucketResponse>> {
|
||||
let mut metrics_accumulator: HashMap<
|
||||
@ -38,14 +38,14 @@ pub async fn get_metrics(
|
||||
let mut set = tokio::task::JoinSet::new();
|
||||
for metric_type in req.metrics.iter().cloned() {
|
||||
let req = req.clone();
|
||||
let merchant_id_scoped = merchant_id.to_owned();
|
||||
let auth_scoped = auth.to_owned();
|
||||
let pool = pool.clone();
|
||||
set.spawn(async move {
|
||||
let data = pool
|
||||
.get_auth_event_metrics(
|
||||
&metric_type,
|
||||
&req.group_by_names.clone(),
|
||||
&merchant_id_scoped,
|
||||
&auth_scoped,
|
||||
&req.filters,
|
||||
req.time_series.map(|t| t.granularity),
|
||||
&req.time_range,
|
||||
@ -130,7 +130,7 @@ pub async fn get_metrics(
|
||||
pub async fn get_filters(
|
||||
pool: &AnalyticsProvider,
|
||||
req: GetAuthEventFilterRequest,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
) -> AnalyticsResult<AuthEventFiltersResponse> {
|
||||
let mut res = AuthEventFiltersResponse::default();
|
||||
for dim in req.group_by_names {
|
||||
@ -139,14 +139,14 @@ pub async fn get_filters(
|
||||
Err(report!(AnalyticsError::UnknownError))
|
||||
}
|
||||
AnalyticsProvider::Clickhouse(pool) => {
|
||||
get_auth_events_filter_for_dimension(dim, merchant_id, &req.time_range, pool)
|
||||
get_auth_events_filter_for_dimension(dim, auth, &req.time_range, pool)
|
||||
.await
|
||||
.map_err(|e| e.change_context(AnalyticsError::UnknownError))
|
||||
}
|
||||
AnalyticsProvider::CombinedCkh(sqlx_pool, ckh_pool) | AnalyticsProvider::CombinedSqlx(sqlx_pool, ckh_pool) => {
|
||||
let ckh_result = get_auth_events_filter_for_dimension(
|
||||
dim,
|
||||
merchant_id,
|
||||
auth,
|
||||
&req.time_range,
|
||||
ckh_pool,
|
||||
)
|
||||
@ -154,7 +154,7 @@ pub async fn get_filters(
|
||||
.map_err(|e| e.change_context(AnalyticsError::UnknownError));
|
||||
let sqlx_result = get_auth_events_filter_for_dimension(
|
||||
dim,
|
||||
merchant_id,
|
||||
auth,
|
||||
&req.time_range,
|
||||
sqlx_pool,
|
||||
)
|
||||
|
||||
@ -6,6 +6,7 @@ use error_stack::ResultExt;
|
||||
use time::PrimitiveDateTime;
|
||||
|
||||
use crate::{
|
||||
enums::AuthInfo,
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, ToSql, Window},
|
||||
types::{
|
||||
AnalyticsCollection, AnalyticsDataSource, DBEnumWrapper, FiltersError, FiltersResult,
|
||||
@ -17,7 +18,7 @@ pub trait AuthEventFilterAnalytics: LoadRow<AuthEventFilterRow> {}
|
||||
|
||||
pub async fn get_auth_events_filter_for_dimension<T>(
|
||||
dimension: AuthEventDimensions,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
time_range: &TimeRange,
|
||||
pool: &T,
|
||||
) -> FiltersResult<Vec<AuthEventFilterRow>>
|
||||
@ -38,12 +39,10 @@ where
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder.set_distinct();
|
||||
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
query_builder
|
||||
.execute_query::<AuthEventFilterRow, _>(pool)
|
||||
.await
|
||||
|
||||
@ -12,6 +12,7 @@ use time::PrimitiveDateTime;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, DBEnumWrapper, LoadRow, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
mod authentication_attempt_count;
|
||||
@ -86,7 +87,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -107,7 +108,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -117,146 +118,62 @@ where
|
||||
match self {
|
||||
Self::AuthenticationCount => {
|
||||
AuthenticationCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::AuthenticationAttemptCount => {
|
||||
AuthenticationAttemptCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::AuthenticationSuccessCount => {
|
||||
AuthenticationSuccessCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::ChallengeFlowCount => {
|
||||
ChallengeFlowCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::ChallengeAttemptCount => {
|
||||
ChallengeAttemptCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::ChallengeSuccessCount => {
|
||||
ChallengeSuccessCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::FrictionlessFlowCount => {
|
||||
FrictionlessFlowCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::FrictionlessSuccessCount => {
|
||||
FrictionlessSuccessCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::AuthenticationErrorMessage => {
|
||||
AuthenticationErrorMessage
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::AuthenticationFunnel => {
|
||||
AuthenticationFunnel
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::AuthenticationExemptionApprovedCount => {
|
||||
AuthenticationExemptionApprovedCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::AuthenticationExemptionRequestedCount => {
|
||||
AuthenticationExemptionRequestedCount
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -30,7 +31,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -65,10 +66,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_in_range_clause(
|
||||
"authentication_status",
|
||||
@ -80,6 +77,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -12,6 +12,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -29,7 +30,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -62,14 +63,12 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
filters.set_filter_clause(&mut query_builder).switch()?;
|
||||
time_range
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -16,6 +16,7 @@ use crate::{
|
||||
ToSql, Window,
|
||||
},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -33,7 +34,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -64,10 +65,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("authentication_status", AuthenticationStatus::Failed)
|
||||
.switch()?;
|
||||
@ -84,6 +81,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -12,6 +12,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -29,7 +30,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -62,10 +63,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause(AuthEventDimensions::ExemptionAccepted, true)
|
||||
.switch()?;
|
||||
@ -74,6 +71,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -12,6 +12,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -29,7 +30,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -62,10 +63,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause(AuthEventDimensions::ExemptionRequested, true)
|
||||
.switch()?;
|
||||
@ -75,6 +72,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -15,6 +15,7 @@ use crate::{
|
||||
Window,
|
||||
},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -32,7 +33,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -65,10 +66,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_custom_filter_clause(
|
||||
AuthEventDimensions::TransactionStatus,
|
||||
@ -81,6 +78,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -13,6 +13,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -30,7 +31,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -63,10 +64,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("authentication_status", AuthenticationStatus::Success)
|
||||
.switch()?;
|
||||
@ -75,6 +72,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -13,6 +13,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -30,7 +31,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -63,10 +64,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause(
|
||||
"authentication_type",
|
||||
@ -85,6 +82,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -13,6 +13,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -30,7 +31,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -63,10 +64,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause(
|
||||
"authentication_type",
|
||||
@ -78,6 +75,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -13,6 +13,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -30,7 +31,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -63,10 +64,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("authentication_status", AuthenticationStatus::Success)
|
||||
.switch()?;
|
||||
@ -82,6 +79,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -13,6 +13,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -30,7 +31,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -63,10 +64,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause(
|
||||
"authentication_type",
|
||||
@ -78,6 +75,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -13,6 +13,7 @@ use super::AuthEventMetricRow;
|
||||
use crate::{
|
||||
query::{Aggregate, GroupByClause, QueryBuilder, QueryFilter, SeriesBucket, ToSql, Window},
|
||||
types::{AnalyticsCollection, AnalyticsDataSource, MetricsError, MetricsResult},
|
||||
AuthInfo,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
@ -30,7 +31,7 @@ where
|
||||
{
|
||||
async fn load_metrics(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
@ -63,10 +64,6 @@ where
|
||||
})
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause("merchant_id", merchant_id)
|
||||
.switch()?;
|
||||
|
||||
query_builder
|
||||
.add_filter_clause(
|
||||
"authentication_type",
|
||||
@ -82,6 +79,7 @@ where
|
||||
.set_filter_clause(&mut query_builder)
|
||||
.attach_printable("Error filtering time range")
|
||||
.switch()?;
|
||||
auth.set_filter_clause(&mut query_builder).switch()?;
|
||||
|
||||
for dim in dimensions.iter() {
|
||||
query_builder
|
||||
|
||||
@ -912,7 +912,7 @@ impl AnalyticsProvider {
|
||||
&self,
|
||||
metric: &AuthEventMetrics,
|
||||
dimensions: &[AuthEventDimensions],
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
auth: &AuthInfo,
|
||||
filters: &AuthEventFilters,
|
||||
granularity: Option<Granularity>,
|
||||
time_range: &TimeRange,
|
||||
@ -921,20 +921,13 @@ impl AnalyticsProvider {
|
||||
Self::Sqlx(_pool) => Err(report!(MetricsError::NotImplemented)),
|
||||
Self::Clickhouse(pool) => {
|
||||
metric
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
time_range,
|
||||
pool,
|
||||
)
|
||||
.load_metrics(auth, dimensions, filters, granularity, time_range, pool)
|
||||
.await
|
||||
}
|
||||
Self::CombinedCkh(_sqlx_pool, ckh_pool) | Self::CombinedSqlx(_sqlx_pool, ckh_pool) => {
|
||||
metric
|
||||
.load_metrics(
|
||||
merchant_id,
|
||||
auth,
|
||||
dimensions,
|
||||
filters,
|
||||
granularity,
|
||||
|
||||
@ -116,11 +116,7 @@ pub mod routes {
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events")
|
||||
.route(web::post().to(get_auth_event_metrics)),
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events/sankey")
|
||||
.route(web::post().to(get_auth_event_sankey)),
|
||||
.route(web::post().to(get_merchant_auth_event_metrics)),
|
||||
)
|
||||
.service(
|
||||
web::resource("filters/auth_events")
|
||||
@ -177,6 +173,10 @@ pub mod routes {
|
||||
web::resource("metrics/sankey")
|
||||
.route(web::post().to(get_merchant_sankey)),
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events/sankey")
|
||||
.route(web::post().to(get_merchant_auth_event_sankey)),
|
||||
)
|
||||
.service(
|
||||
web::scope("/merchant")
|
||||
.service(
|
||||
@ -191,6 +191,10 @@ pub mod routes {
|
||||
web::resource("metrics/refunds")
|
||||
.route(web::post().to(get_merchant_refund_metrics)),
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events")
|
||||
.route(web::post().to(get_merchant_auth_event_metrics)),
|
||||
)
|
||||
.service(
|
||||
web::resource("filters/payments")
|
||||
.route(web::post().to(get_merchant_payment_filters)),
|
||||
@ -203,6 +207,10 @@ pub mod routes {
|
||||
web::resource("filters/refunds")
|
||||
.route(web::post().to(get_merchant_refund_filters)),
|
||||
)
|
||||
.service(
|
||||
web::resource("filters/auth_events")
|
||||
.route(web::post().to(get_merchant_auth_events_filters)),
|
||||
)
|
||||
.service(
|
||||
web::resource("{domain}/info").route(web::get().to(get_info)),
|
||||
)
|
||||
@ -242,6 +250,10 @@ pub mod routes {
|
||||
.service(
|
||||
web::resource("metrics/sankey")
|
||||
.route(web::post().to(get_merchant_sankey)),
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events/sankey")
|
||||
.route(web::post().to(get_merchant_auth_event_sankey)),
|
||||
),
|
||||
)
|
||||
.service(
|
||||
@ -277,10 +289,18 @@ pub mod routes {
|
||||
web::resource("metrics/disputes")
|
||||
.route(web::post().to(get_org_dispute_metrics)),
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events")
|
||||
.route(web::post().to(get_org_auth_event_metrics)),
|
||||
)
|
||||
.service(
|
||||
web::resource("filters/disputes")
|
||||
.route(web::post().to(get_org_dispute_filters)),
|
||||
)
|
||||
.service(
|
||||
web::resource("filters/auth_events")
|
||||
.route(web::post().to(get_org_auth_events_filters)),
|
||||
)
|
||||
.service(
|
||||
web::resource("report/dispute")
|
||||
.route(web::post().to(generate_org_dispute_report)),
|
||||
@ -300,6 +320,10 @@ pub mod routes {
|
||||
.service(
|
||||
web::resource("metrics/sankey")
|
||||
.route(web::post().to(get_org_sankey)),
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events/sankey")
|
||||
.route(web::post().to(get_org_auth_event_sankey)),
|
||||
),
|
||||
)
|
||||
.service(
|
||||
@ -335,10 +359,18 @@ pub mod routes {
|
||||
web::resource("metrics/disputes")
|
||||
.route(web::post().to(get_profile_dispute_metrics)),
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events")
|
||||
.route(web::post().to(get_profile_auth_event_metrics)),
|
||||
)
|
||||
.service(
|
||||
web::resource("filters/disputes")
|
||||
.route(web::post().to(get_profile_dispute_filters)),
|
||||
)
|
||||
.service(
|
||||
web::resource("filters/auth_events")
|
||||
.route(web::post().to(get_profile_auth_events_filters)),
|
||||
)
|
||||
.service(
|
||||
web::resource("connector_event_logs")
|
||||
.route(web::get().to(get_profile_connector_events)),
|
||||
@ -379,6 +411,10 @@ pub mod routes {
|
||||
.service(
|
||||
web::resource("metrics/sankey")
|
||||
.route(web::post().to(get_profile_sankey)),
|
||||
)
|
||||
.service(
|
||||
web::resource("metrics/auth_events/sankey")
|
||||
.route(web::post().to(get_profile_auth_event_sankey)),
|
||||
),
|
||||
),
|
||||
)
|
||||
@ -1043,7 +1079,7 @@ pub mod routes {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `json_payload` array does not contain one `GetAuthEventMetricRequest` element.
|
||||
pub async fn get_auth_event_metrics(
|
||||
pub async fn get_merchant_auth_event_metrics(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
json_payload: web::Json<[GetAuthEventMetricRequest; 1]>,
|
||||
@ -1062,13 +1098,16 @@ pub mod routes {
|
||||
&req,
|
||||
payload,
|
||||
|state, auth: AuthenticationData, req, _| async move {
|
||||
analytics::auth_events::get_metrics(
|
||||
&state.pool,
|
||||
auth.merchant_account.get_id(),
|
||||
req,
|
||||
)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
let org_id = auth.merchant_account.get_org_id();
|
||||
let merchant_id = auth.merchant_account.get_id();
|
||||
let auth: AuthInfo = AuthInfo::MerchantLevel {
|
||||
org_id: org_id.clone(),
|
||||
merchant_ids: vec![merchant_id.clone()],
|
||||
};
|
||||
|
||||
analytics::auth_events::get_metrics(&state.pool, &auth, req)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::MerchantAnalyticsRead,
|
||||
@ -1078,6 +1117,98 @@ pub mod routes {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `json_payload` array does not contain one `GetAuthEventMetricRequest` element.
|
||||
pub async fn get_profile_auth_event_metrics(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
json_payload: web::Json<[GetAuthEventMetricRequest; 1]>,
|
||||
) -> impl Responder {
|
||||
// safety: This shouldn't panic owing to the data type
|
||||
#[allow(clippy::expect_used)]
|
||||
let payload = json_payload
|
||||
.into_inner()
|
||||
.to_vec()
|
||||
.pop()
|
||||
.expect("Couldn't get GetAuthEventMetricRequest");
|
||||
let flow = AnalyticsFlow::GetAuthMetrics;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, auth: AuthenticationData, req, _| async move {
|
||||
let org_id = auth.merchant_account.get_org_id();
|
||||
let merchant_id = auth.merchant_account.get_id();
|
||||
let profile_id = auth
|
||||
.profile_id
|
||||
.ok_or(report!(UserErrors::JwtProfileIdMissing))
|
||||
.change_context(AnalyticsError::AccessForbiddenError)?;
|
||||
let auth: AuthInfo = AuthInfo::ProfileLevel {
|
||||
org_id: org_id.clone(),
|
||||
merchant_id: merchant_id.clone(),
|
||||
profile_ids: vec![profile_id.clone()],
|
||||
};
|
||||
analytics::auth_events::get_metrics(&state.pool, &auth, req)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::ProfileAnalyticsRead,
|
||||
},
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `json_payload` array does not contain one `GetAuthEventMetricRequest` element.
|
||||
pub async fn get_org_auth_event_metrics(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
json_payload: web::Json<[GetAuthEventMetricRequest; 1]>,
|
||||
) -> impl Responder {
|
||||
// safety: This shouldn't panic owing to the data type
|
||||
#[allow(clippy::expect_used)]
|
||||
let payload = json_payload
|
||||
.into_inner()
|
||||
.to_vec()
|
||||
.pop()
|
||||
.expect("Couldn't get GetAuthEventMetricRequest");
|
||||
let flow = AnalyticsFlow::GetAuthMetrics;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, auth: AuthenticationData, req, _| async move {
|
||||
let org_id = auth.merchant_account.get_org_id();
|
||||
let auth: AuthInfo = AuthInfo::OrgLevel {
|
||||
org_id: org_id.clone(),
|
||||
};
|
||||
analytics::auth_events::get_metrics(&state.pool, &auth, req)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::PlatformOrgAdminAuth {
|
||||
is_admin_auth_allowed: false,
|
||||
organization_id: None,
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::OrganizationAnalyticsRead,
|
||||
},
|
||||
req.headers(),
|
||||
),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_merchant_payment_filters(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
@ -1120,13 +1251,16 @@ pub mod routes {
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
|state, auth: AuthenticationData, req, _| async move {
|
||||
analytics::auth_events::get_filters(
|
||||
&state.pool,
|
||||
req,
|
||||
auth.merchant_account.get_id(),
|
||||
)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
let org_id = auth.merchant_account.get_org_id();
|
||||
let merchant_id = auth.merchant_account.get_id();
|
||||
|
||||
let auth: AuthInfo = AuthInfo::MerchantLevel {
|
||||
org_id: org_id.clone(),
|
||||
merchant_ids: vec![merchant_id.clone()],
|
||||
};
|
||||
analytics::auth_events::get_filters(&state.pool, req, &auth)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::MerchantAnalyticsRead,
|
||||
@ -1136,6 +1270,80 @@ pub mod routes {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
pub async fn get_org_auth_events_filters(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
json_payload: web::Json<GetAuthEventFilterRequest>,
|
||||
) -> impl Responder {
|
||||
let flow = AnalyticsFlow::GetAuthEventFilters;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
|state, auth: AuthenticationData, req, _| async move {
|
||||
let org_id = auth.merchant_account.get_org_id();
|
||||
let auth: AuthInfo = AuthInfo::OrgLevel {
|
||||
org_id: org_id.clone(),
|
||||
};
|
||||
|
||||
analytics::auth_events::get_filters(&state.pool, req, &auth)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::PlatformOrgAdminAuth {
|
||||
is_admin_auth_allowed: false,
|
||||
organization_id: None,
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::OrganizationAnalyticsRead,
|
||||
},
|
||||
req.headers(),
|
||||
),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
pub async fn get_profile_auth_events_filters(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
json_payload: web::Json<GetAuthEventFilterRequest>,
|
||||
) -> impl Responder {
|
||||
let flow = AnalyticsFlow::GetAuthEventFilters;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
json_payload.into_inner(),
|
||||
|state, auth: AuthenticationData, req, _| async move {
|
||||
let org_id = auth.merchant_account.get_org_id();
|
||||
let merchant_id = auth.merchant_account.get_id();
|
||||
let profile_id = auth
|
||||
.profile_id
|
||||
.ok_or(report!(UserErrors::JwtProfileIdMissing))
|
||||
.change_context(AnalyticsError::AccessForbiddenError)?;
|
||||
|
||||
let auth: AuthInfo = AuthInfo::ProfileLevel {
|
||||
org_id: org_id.clone(),
|
||||
merchant_id: merchant_id.clone(),
|
||||
profile_ids: vec![profile_id.clone()],
|
||||
};
|
||||
analytics::auth_events::get_filters(&state.pool, req, &auth)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::ProfileAnalyticsRead,
|
||||
},
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
pub async fn get_org_payment_filters(
|
||||
state: web::Data<AppState>,
|
||||
@ -2809,7 +3017,7 @@ pub mod routes {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_auth_event_sankey(
|
||||
pub async fn get_merchant_auth_event_sankey(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
json_payload: web::Json<TimeRange>,
|
||||
@ -2840,6 +3048,80 @@ pub mod routes {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
pub async fn get_org_auth_event_sankey(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
json_payload: web::Json<TimeRange>,
|
||||
) -> impl Responder {
|
||||
let flow = AnalyticsFlow::GetSankey;
|
||||
let payload = json_payload.into_inner();
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, auth: AuthenticationData, req, _| async move {
|
||||
let org_id = auth.merchant_account.get_org_id();
|
||||
let auth: AuthInfo = AuthInfo::OrgLevel {
|
||||
org_id: org_id.clone(),
|
||||
};
|
||||
analytics::auth_events::get_sankey(&state.pool, &auth, req)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::PlatformOrgAdminAuth {
|
||||
is_admin_auth_allowed: false,
|
||||
organization_id: None,
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::OrganizationAnalyticsRead,
|
||||
},
|
||||
req.headers(),
|
||||
),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
pub async fn get_profile_auth_event_sankey(
|
||||
state: web::Data<AppState>,
|
||||
req: actix_web::HttpRequest,
|
||||
json_payload: web::Json<TimeRange>,
|
||||
) -> impl Responder {
|
||||
let flow = AnalyticsFlow::GetSankey;
|
||||
let payload = json_payload.into_inner();
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state, auth: AuthenticationData, req, _| async move {
|
||||
let org_id = auth.merchant_account.get_org_id();
|
||||
let merchant_id = auth.merchant_account.get_id();
|
||||
let profile_id = auth
|
||||
.profile_id
|
||||
.ok_or(report!(UserErrors::JwtProfileIdMissing))
|
||||
.change_context(AnalyticsError::AccessForbiddenError)?;
|
||||
let auth: AuthInfo = AuthInfo::ProfileLevel {
|
||||
org_id: org_id.clone(),
|
||||
merchant_id: merchant_id.clone(),
|
||||
profile_ids: vec![profile_id.clone()],
|
||||
};
|
||||
analytics::auth_events::get_sankey(&state.pool, &auth, req)
|
||||
.await
|
||||
.map(ApplicationResponse::Json)
|
||||
},
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::ProfileAnalyticsRead,
|
||||
},
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
pub async fn get_org_sankey(
|
||||
state: web::Data<AppState>,
|
||||
|
||||
Reference in New Issue
Block a user