Files
hyperswitch/crates/analytics/src/sdk_events/core.rs
Vrishab Srivatsa 24d154248c feat(analytics): authentication analytics (#4429)
Co-authored-by: Sampras Lopes <lsampras@pm.me>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
2024-05-10 10:08:26 +00:00

204 lines
7.5 KiB
Rust

use std::collections::HashMap;
use api_models::analytics::{
sdk_events::{
MetricsBucketResponse, SdkEventMetrics, SdkEventMetricsBucketIdentifier, SdkEventsRequest,
},
AnalyticsMetadata, GetSdkEventFiltersRequest, GetSdkEventMetricRequest, MetricsResponse,
SdkEventFiltersResponse,
};
use common_utils::errors::ReportSwitchExt;
use error_stack::ResultExt;
use router_env::{instrument, logger, tracing};
use super::{
events::{get_sdk_event, SdkEventsResult},
SdkEventMetricsAccumulator,
};
use crate::{
errors::{AnalyticsError, AnalyticsResult},
sdk_events::SdkEventMetricAccumulator,
types::FiltersError,
AnalyticsProvider,
};
#[instrument(skip_all)]
pub async fn sdk_events_core(
pool: &AnalyticsProvider,
req: SdkEventsRequest,
publishable_key: String,
) -> AnalyticsResult<Vec<SdkEventsResult>> {
match pool {
AnalyticsProvider::Sqlx(_) => Err(FiltersError::NotImplemented(
"SDK Events not implemented for SQLX",
))
.attach_printable("SQL Analytics is not implemented for Sdk Events"),
AnalyticsProvider::Clickhouse(pool) => get_sdk_event(&publishable_key, req, pool).await,
AnalyticsProvider::CombinedSqlx(_sqlx_pool, ckh_pool)
| AnalyticsProvider::CombinedCkh(_sqlx_pool, ckh_pool) => {
get_sdk_event(&publishable_key, req, ckh_pool).await
}
}
.switch()
}
#[instrument(skip_all)]
pub async fn get_metrics(
pool: &AnalyticsProvider,
publishable_key: Option<&String>,
req: GetSdkEventMetricRequest,
) -> AnalyticsResult<MetricsResponse<MetricsBucketResponse>> {
let mut metrics_accumulator: HashMap<
SdkEventMetricsBucketIdentifier,
SdkEventMetricsAccumulator,
> = HashMap::new();
if let Some(publishable_key) = publishable_key {
let mut set = tokio::task::JoinSet::new();
for metric_type in req.metrics.iter().cloned() {
let req = req.clone();
let publishable_key_scoped = publishable_key.to_owned();
let pool = pool.clone();
set.spawn(async move {
let data = pool
.get_sdk_event_metrics(
&metric_type,
&req.group_by_names.clone(),
&publishable_key_scoped,
&req.filters,
&req.time_series.map(|t| t.granularity),
&req.time_range,
)
.await
.change_context(AnalyticsError::UnknownError);
(metric_type, data)
});
}
while let Some((metric, data)) = set
.join_next()
.await
.transpose()
.change_context(AnalyticsError::UnknownError)?
{
logger::info!("Logging Result {:?}", data);
for (id, value) in data? {
let metrics_builder = metrics_accumulator.entry(id).or_default();
match metric {
SdkEventMetrics::PaymentAttempts => {
metrics_builder.payment_attempts.add_metrics_bucket(&value)
}
SdkEventMetrics::PaymentMethodsCallCount => metrics_builder
.payment_methods_call_count
.add_metrics_bucket(&value),
SdkEventMetrics::SdkRenderedCount => metrics_builder
.sdk_rendered_count
.add_metrics_bucket(&value),
SdkEventMetrics::SdkInitiatedCount => metrics_builder
.sdk_initiated_count
.add_metrics_bucket(&value),
SdkEventMetrics::PaymentMethodSelectedCount => metrics_builder
.payment_method_selected_count
.add_metrics_bucket(&value),
SdkEventMetrics::PaymentDataFilledCount => metrics_builder
.payment_data_filled_count
.add_metrics_bucket(&value),
SdkEventMetrics::AveragePaymentTime => metrics_builder
.average_payment_time
.add_metrics_bucket(&value),
SdkEventMetrics::LoadTime => {
metrics_builder.load_time.add_metrics_bucket(&value)
}
}
}
logger::debug!(
"Analytics Accumulated Results: metric: {}, results: {:#?}",
metric,
metrics_accumulator
);
}
let query_data: Vec<MetricsBucketResponse> = metrics_accumulator
.into_iter()
.map(|(id, val)| MetricsBucketResponse {
values: val.collect(),
dimensions: id,
})
.collect();
Ok(MetricsResponse {
query_data,
meta_data: [AnalyticsMetadata {
current_time_range: req.time_range,
}],
})
} else {
logger::error!("Publishable key not present for merchant ID");
Ok(MetricsResponse {
query_data: vec![],
meta_data: [AnalyticsMetadata {
current_time_range: req.time_range,
}],
})
}
}
#[allow(dead_code)]
pub async fn get_filters(
pool: &AnalyticsProvider,
req: GetSdkEventFiltersRequest,
publishable_key: Option<&String>,
) -> AnalyticsResult<SdkEventFiltersResponse> {
use api_models::analytics::{sdk_events::SdkEventDimensions, SdkEventFilterValue};
use super::filters::get_sdk_event_filter_for_dimension;
use crate::sdk_events::filters::SdkEventFilter;
let mut res = SdkEventFiltersResponse::default();
if let Some(publishable_key) = publishable_key {
for dim in req.group_by_names {
let values = match pool {
AnalyticsProvider::Sqlx(_pool) => Err(FiltersError::NotImplemented(
"SDK Events not implemented for SQLX",
))
.attach_printable("SQL Analytics is not implemented for SDK Events"),
AnalyticsProvider::Clickhouse(pool) => {
get_sdk_event_filter_for_dimension(dim, publishable_key, &req.time_range, pool)
.await
}
AnalyticsProvider::CombinedSqlx(_sqlx_pool, ckh_pool)
| AnalyticsProvider::CombinedCkh(_sqlx_pool, ckh_pool) => {
get_sdk_event_filter_for_dimension(
dim,
publishable_key,
&req.time_range,
ckh_pool,
)
.await
}
}
.change_context(AnalyticsError::UnknownError)?
.into_iter()
.filter_map(|fil: SdkEventFilter| match dim {
SdkEventDimensions::PaymentMethod => fil.payment_method,
SdkEventDimensions::Platform => fil.platform,
SdkEventDimensions::BrowserName => fil.browser_name,
SdkEventDimensions::Source => fil.source,
SdkEventDimensions::Component => fil.component,
SdkEventDimensions::PaymentExperience => fil.payment_experience,
})
.collect::<Vec<String>>();
res.query_data.push(SdkEventFilterValue {
dimension: dim,
values,
})
}
} else {
router_env::logger::error!("Publishable key not found for merchant");
}
Ok(res)
}