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> { 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> { 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 = 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 { 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::>(); res.query_data.push(SdkEventFilterValue { dimension: dim, values, }) } } else { router_env::logger::error!("Publishable key not found for merchant"); } Ok(res) }