Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d42ddd1
move(analytics): init
ivor11 Oct 24, 2023
bff5524
move(analytics): AnalyticsFlow, API key Auth refactored
ivor11 Oct 24, 2023
df72fb2
move(analytics): compiled analytics
ivor11 Oct 24, 2023
6c25974
chore: run formatter
github-actions[bot] Oct 24, 2023
f5cbc6a
Merge branch 'main' into move/analytics
ivor11 Oct 24, 2023
1a1300d
move(analytics): fix clippy issues
ivor11 Oct 25, 2023
59cb12a
move(analytics): fix clippy issues2
ivor11 Oct 25, 2023
102e727
move(analytics): fix clippy issues3
ivor11 Oct 25, 2023
375f9e9
chore: run formatter
github-actions[bot] Oct 25, 2023
fc675cc
move(analytics): fix clippy issues4
ivor11 Oct 25, 2023
9c2f36e
chore: run formatter
github-actions[bot] Oct 25, 2023
cc6ede1
Merge branch 'main' into move/analytics
ivor11 Oct 25, 2023
5bf0b90
Merge branch 'main' into move/analytics
ivor11 Oct 27, 2023
f105fd4
move(analytics): resolved conflicts
ivor11 Oct 27, 2023
7b2c721
Merge remote-tracking branch 'origin' into move/analytics
ivor11 Oct 30, 2023
458774d
move(analytics): merge with main
ivor11 Oct 31, 2023
52fb019
Merge branch 'main' into move/analytics
ivor11 Oct 31, 2023
5d98391
move(analytics): format & clean
ivor11 Oct 31, 2023
0d72eb7
Merge branch 'main' into move/analytics
ivor11 Oct 31, 2023
5f12b3a
Merge branch 'main' into move/analytics
ivor11 Nov 2, 2023
9d7ceeb
merge with main
ivor11 Nov 6, 2023
7569240
move(analytics): pub mod analytics
ivor11 Nov 6, 2023
46302c3
Merge remote-tracking branch 'origin' into move/analytics
ivor11 Nov 6, 2023
9343e04
move(analytics): Default for SqlxClient
ivor11 Nov 6, 2023
d051f96
chore: run formatter
github-actions[bot] Nov 6, 2023
a9d37bb
Merge branch 'main' into move/analytics
ivor11 Nov 6, 2023
1c86f16
Merge branch 'main' into move/analytics
ivor11 Nov 6, 2023
06e95b6
move(analytics): addressed clippy issues
ivor11 Nov 6, 2023
181cec8
Merge remote-tracking branch 'origin' into move/analytics
ivor11 Nov 7, 2023
5fc7163
Merge branch 'main' into move/analytics
ivor11 Nov 7, 2023
ddfb1df
Merge branch 'main' into move/analytics
ivor11 Nov 7, 2023
997a97e
Merge branch 'main' into move/analytics
lsampras Nov 8, 2023
a5b5daa
Merge branch 'main' into move/analytics
ivor11 Nov 9, 2023
25ff040
move(analytics): resolving build issues after merge with main
ivor11 Nov 9, 2023
a6c09ce
chore: run formatter
github-actions[bot] Nov 9, 2023
d41e8e5
Merge branch 'main' into move/analytics
ivor11 Nov 9, 2023
1697364
move(analytics): added olap to release target
ivor11 Nov 9, 2023
b4f9a34
Merge branch 'main' into move/analytics
ivor11 Nov 9, 2023
3aa71a8
Merge branch 'main' into move/analytics
ivor11 Nov 9, 2023
485b455
Merge branch 'main' into move/analytics
ivor11 Nov 10, 2023
520c048
Merge branch 'main' into move/analytics
ivor11 Nov 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
281 changes: 270 additions & 11 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion config/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -434,10 +434,22 @@ apple_pay_ppc_key = "APPLE_PAY_PAYMENT_PROCESSING_CERTIFICATE_KEY" #Private
apple_pay_merchant_cert = "APPLE_PAY_MERCHNAT_CERTIFICATE" #Merchant Certificate provided by Apple Pay (https://developer.apple.com/) Certificates, Identifiers & Profiles > Apple Pay Merchant Identity Certificate
apple_pay_merchant_cert_key = "APPLE_PAY_MERCHNAT_CERTIFICATE_KEY" #Private key generate by RSA:2048 algorithm


[payment_link]
sdk_url = "http://localhost:9090/dist/HyperLoader.js"

# Analytics configuration.
[analytics]
source = "sqlx" # The Analytics source/strategy to be used

[analytics.sqlx]
username = "db_user" # Analytics DB Username
password = "db_pass" # Analytics DB Password
host = "localhost" # Analytics DB Host
port = 5432 # Analytics DB Port
dbname = "hyperswitch_db" # Name of Database
pool_size = 5 # Number of connections to keep open
connection_timeout = 10 # Timeout for database connection in seconds

# Config for KV setup
[kv_config]
# TTL for KV in seconds
Expand Down
11 changes: 11 additions & 0 deletions config/docker_compose.toml
Original file line number Diff line number Diff line change
Expand Up @@ -319,5 +319,16 @@ supported_connectors = "braintree"
redis_lock_expiry_seconds = 180 # 3 * 60 seconds
delay_between_retries_in_milliseconds = 500

[analytics]
source = "sqlx"

[analytics.sqlx]
username = "db_user"
password = "db_pass"
host = "pg"
port = 5432
dbname = "hyperswitch_db"
pool_size = 5

[kv_config]
ttl = 900 # 15 * 60 seconds
137 changes: 137 additions & 0 deletions crates/api_models/src/analytics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::collections::HashSet;

use time::PrimitiveDateTime;

use self::{
payments::{PaymentDimensions, PaymentMetrics},
refunds::{RefundDimensions, RefundMetrics},
};

pub mod payments;
pub mod refunds;

#[derive(Debug, serde::Serialize)]
pub struct NameDescription {
pub name: String,
pub desc: String,
}

#[derive(Debug, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetInfoResponse {
pub metrics: Vec<NameDescription>,
pub download_dimensions: Option<Vec<NameDescription>>,
pub dimensions: Vec<NameDescription>,
}

#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct TimeRange {
#[serde(with = "common_utils::custom_serde::iso8601")]
pub start_time: PrimitiveDateTime,
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
pub end_time: Option<PrimitiveDateTime>,
}

#[derive(Clone, Copy, Debug, serde::Deserialize, masking::Serialize)]
pub struct TimeSeries {
pub granularity: Granularity,
}

#[derive(Clone, Copy, Debug, serde::Deserialize, masking::Serialize)]
pub enum Granularity {
#[serde(rename = "G_ONEMIN")]
OneMin,
#[serde(rename = "G_FIVEMIN")]
FiveMin,
#[serde(rename = "G_FIFTEENMIN")]
FifteenMin,
#[serde(rename = "G_THIRTYMIN")]
ThirtyMin,
#[serde(rename = "G_ONEHOUR")]
OneHour,
#[serde(rename = "G_ONEDAY")]
OneDay,
}

#[derive(Clone, Debug, serde::Deserialize, masking::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetPaymentMetricRequest {
pub time_series: Option<TimeSeries>,
pub time_range: TimeRange,
#[serde(default)]
pub group_by_names: Vec<PaymentDimensions>,
#[serde(default)]
pub filters: payments::PaymentFilters,
pub metrics: HashSet<PaymentMetrics>,
#[serde(default)]
pub delta: bool,
}

#[derive(Clone, Debug, serde::Deserialize, masking::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetRefundMetricRequest {
pub time_series: Option<TimeSeries>,
pub time_range: TimeRange,
#[serde(default)]
pub group_by_names: Vec<RefundDimensions>,
#[serde(default)]
pub filters: refunds::RefundFilters,
pub metrics: HashSet<RefundMetrics>,
#[serde(default)]
pub delta: bool,
}

#[derive(Debug, serde::Serialize)]
pub struct AnalyticsMetadata {
pub current_time_range: TimeRange,
}

#[derive(Debug, serde::Deserialize, masking::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetPaymentFiltersRequest {
pub time_range: TimeRange,
#[serde(default)]
pub group_by_names: Vec<PaymentDimensions>,
}

#[derive(Debug, Default, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentFiltersResponse {
pub query_data: Vec<FilterValue>,
}

#[derive(Debug, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FilterValue {
pub dimension: PaymentDimensions,
pub values: Vec<String>,
}

#[derive(Debug, serde::Deserialize, masking::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetRefundFilterRequest {
pub time_range: TimeRange,
#[serde(default)]
pub group_by_names: Vec<RefundDimensions>,
}

#[derive(Debug, Default, serde::Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RefundFiltersResponse {
pub query_data: Vec<RefundFilterValue>,
}

#[derive(Debug, serde::Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RefundFilterValue {
pub dimension: RefundDimensions,
pub values: Vec<String>,
}

#[derive(Debug, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricsResponse<T> {
pub query_data: Vec<T>,
pub meta_data: [AnalyticsMetadata; 1],
}
176 changes: 176 additions & 0 deletions crates/api_models/src/analytics/payments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};

use common_enums::enums::{AttemptStatus, AuthenticationType, Currency, PaymentMethod};

use super::{NameDescription, TimeRange};
use crate::enums::Connector;

#[derive(Clone, Debug, Default, serde::Deserialize, masking::Serialize)]
pub struct PaymentFilters {
#[serde(default)]
pub currency: Vec<Currency>,
#[serde(default)]
pub status: Vec<AttemptStatus>,
#[serde(default)]
pub connector: Vec<Connector>,
#[serde(default)]
pub auth_type: Vec<AuthenticationType>,
#[serde(default)]
pub payment_method: Vec<PaymentMethod>,
}

#[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 PaymentDimensions {
// Do not change the order of these enums
// Consult the Dashboard FE folks since these also affects the order of metrics on FE
Connector,
PaymentMethod,
Currency,
#[strum(serialize = "authentication_type")]
#[serde(rename = "authentication_type")]
AuthType,
#[strum(serialize = "status")]
#[serde(rename = "status")]
PaymentStatus,
}

#[derive(
Clone,
Debug,
Hash,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
strum::Display,
strum::EnumIter,
strum::AsRefStr,
)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum PaymentMetrics {
PaymentSuccessRate,
PaymentCount,
PaymentSuccessCount,
PaymentProcessedAmount,
AvgTicketSize,
}

pub mod metric_behaviour {
pub struct PaymentSuccessRate;
pub struct PaymentCount;
pub struct PaymentSuccessCount;
pub struct PaymentProcessedAmount;
pub struct AvgTicketSize;
}

impl From<PaymentMetrics> for NameDescription {
fn from(value: PaymentMetrics) -> Self {
Self {
name: value.to_string(),
desc: String::new(),
}
}
}

impl From<PaymentDimensions> for NameDescription {
fn from(value: PaymentDimensions) -> Self {
Self {
name: value.to_string(),
desc: String::new(),
}
}
}

#[derive(Debug, serde::Serialize, Eq)]
pub struct PaymentMetricsBucketIdentifier {
pub currency: Option<Currency>,
pub status: Option<AttemptStatus>,
pub connector: Option<String>,
#[serde(rename = "authentication_type")]
pub auth_type: Option<AuthenticationType>,
pub payment_method: Option<String>,
#[serde(rename = "time_range")]
pub time_bucket: TimeRange,
// Coz FE sucks
#[serde(rename = "time_bucket")]
#[serde(with = "common_utils::custom_serde::iso8601custom")]
pub start_time: time::PrimitiveDateTime,
}

impl PaymentMetricsBucketIdentifier {
pub fn new(
currency: Option<Currency>,
status: Option<AttemptStatus>,
connector: Option<String>,
auth_type: Option<AuthenticationType>,
payment_method: Option<String>,
normalized_time_range: TimeRange,
) -> Self {
Self {
currency,
status,
connector,
auth_type,
payment_method,
time_bucket: normalized_time_range,
start_time: normalized_time_range.start_time,
}
}
}

impl Hash for PaymentMetricsBucketIdentifier {
fn hash<H: Hasher>(&self, state: &mut H) {
self.currency.hash(state);
self.status.map(|i| i.to_string()).hash(state);
self.connector.hash(state);
self.auth_type.map(|i| i.to_string()).hash(state);
self.payment_method.hash(state);
self.time_bucket.hash(state);
}
}

impl PartialEq for PaymentMetricsBucketIdentifier {
fn eq(&self, other: &Self) -> bool {
let mut left = DefaultHasher::new();
self.hash(&mut left);
let mut right = DefaultHasher::new();
other.hash(&mut right);
left.finish() == right.finish()
}
}

#[derive(Debug, serde::Serialize)]
pub struct PaymentMetricsBucketValue {
pub payment_success_rate: Option<f64>,
pub payment_count: Option<u64>,
pub payment_success_count: Option<u64>,
pub payment_processed_amount: Option<u64>,
pub avg_ticket_size: Option<f64>,
}

#[derive(Debug, serde::Serialize)]
pub struct MetricsBucketResponse {
#[serde(flatten)]
pub values: PaymentMetricsBucketValue,
#[serde(flatten)]
pub dimensions: PaymentMetricsBucketIdentifier,
}
Loading