use super::{AnalyticsError, AnalyticsEvent};
use crate::storage::AuthStorage;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsConfig {
pub collection_interval: u64,
pub retention_days: u32,
pub detailed_metrics: bool,
pub performance_profiling: bool,
}
impl Default for MetricsConfig {
fn default() -> Self {
Self {
collection_interval: 60,
retention_days: 90,
detailed_metrics: true,
performance_profiling: false,
}
}
}
pub struct MetricsCollector {
_config: MetricsConfig,
storage: Arc<dyn AuthStorage>,
current_metrics: HashMap<String, f64>,
}
impl MetricsCollector {
pub fn new(config: MetricsConfig, storage: Arc<dyn AuthStorage>) -> Self {
Self {
_config: config,
storage,
current_metrics: HashMap::new(),
}
}
pub async fn collect_metrics(
&mut self,
events: &[AnalyticsEvent],
) -> Result<(), AnalyticsError> {
for event in events {
match event.event_type {
crate::analytics::RbacEventType::PermissionCheck => {
*self
.current_metrics
.entry("permission_checks_total".to_string())
.or_insert(0.0) += 1.0;
if let Some(action) = &event.action {
if action == "Granted" {
*self
.current_metrics
.entry("permission_grants_total".to_string())
.or_insert(0.0) += 1.0;
} else {
*self
.current_metrics
.entry("permission_denies_total".to_string())
.or_insert(0.0) += 1.0;
}
}
}
crate::analytics::RbacEventType::RoleAssignment => {
*self
.current_metrics
.entry("role_assignments_total".to_string())
.or_insert(0.0) += 1.0;
}
_ => {
*self
.current_metrics
.entry("other_events_total".to_string())
.or_insert(0.0) += 1.0;
}
}
}
let json_data = serde_json::to_vec(&self.current_metrics).unwrap_or_default();
let _ = self
.storage
.store_kv("current_metrics_snapshot", &json_data, None)
.await;
Ok(())
}
pub fn get_current_metrics(&self) -> &HashMap<String, f64> {
&self.current_metrics
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_config_default() {
let config = MetricsConfig::default();
assert_eq!(config.collection_interval, 60);
assert_eq!(config.retention_days, 90);
assert!(config.detailed_metrics);
assert!(!config.performance_profiling);
}
#[test]
fn test_metrics_collector_starts_empty() {
let collector = MetricsCollector::new(
MetricsConfig::default(),
std::sync::Arc::new(crate::storage::MemoryStorage::new()),
);
assert!(collector.get_current_metrics().is_empty());
}
#[tokio::test]
async fn test_collect_metrics_no_op_succeeds() {
let mut collector = MetricsCollector::new(
MetricsConfig::default(),
std::sync::Arc::new(crate::storage::MemoryStorage::new()),
);
let result = collector.collect_metrics(&[]).await;
assert!(result.is_ok());
assert!(collector.get_current_metrics().is_empty());
}
}