Skip to main content

auth_framework/analytics/
metrics.rs

1//! RBAC Metrics Collection and Processing
2//!
3//! This module provides metrics collection, aggregation, and analysis
4//! for RBAC system performance and usage patterns.
5//!
6//! > **Status:** Event persistence is wired up today, and higher-level metrics
7//! > currently reflect the analytics events collected so far.
8
9use super::{AnalyticsError, AnalyticsEvent};
10use crate::storage::AuthStorage;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::sync::Arc;
14
15/// Metrics collector configuration
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct MetricsConfig {
18    /// Collection interval in seconds
19    pub collection_interval: u64,
20
21    /// Retention period in days
22    pub retention_days: u32,
23
24    /// Enable detailed metrics
25    pub detailed_metrics: bool,
26
27    /// Enable performance profiling
28    pub performance_profiling: bool,
29}
30
31impl Default for MetricsConfig {
32    fn default() -> Self {
33        Self {
34            collection_interval: 60,
35            retention_days: 90,
36            detailed_metrics: true,
37            performance_profiling: false,
38        }
39    }
40}
41
42/// Metrics collector
43pub struct MetricsCollector {
44    _config: MetricsConfig,
45    storage: Arc<dyn AuthStorage>,
46    current_metrics: HashMap<String, f64>,
47}
48
49impl MetricsCollector {
50    /// Create new metrics collector
51    pub fn new(config: MetricsConfig, storage: Arc<dyn AuthStorage>) -> Self {
52        Self {
53            _config: config,
54            storage,
55            current_metrics: HashMap::new(),
56        }
57    }
58
59    /// Collect metrics from events
60    pub async fn collect_metrics(
61        &mut self,
62        events: &[AnalyticsEvent],
63    ) -> Result<(), AnalyticsError> {
64        for event in events {
65            match event.event_type {
66                crate::analytics::RbacEventType::PermissionCheck => {
67                    *self
68                        .current_metrics
69                        .entry("permission_checks_total".to_string())
70                        .or_insert(0.0) += 1.0;
71                    if let Some(action) = &event.action {
72                        if action == "Granted" {
73                            *self
74                                .current_metrics
75                                .entry("permission_grants_total".to_string())
76                                .or_insert(0.0) += 1.0;
77                        } else {
78                            *self
79                                .current_metrics
80                                .entry("permission_denies_total".to_string())
81                                .or_insert(0.0) += 1.0;
82                        }
83                    }
84                }
85                crate::analytics::RbacEventType::RoleAssignment => {
86                    *self
87                        .current_metrics
88                        .entry("role_assignments_total".to_string())
89                        .or_insert(0.0) += 1.0;
90                }
91                _ => {
92                    *self
93                        .current_metrics
94                        .entry("other_events_total".to_string())
95                        .or_insert(0.0) += 1.0;
96                }
97            }
98        }
99
100        let json_data = serde_json::to_vec(&self.current_metrics).unwrap_or_default();
101        let _ = self
102            .storage
103            .store_kv("current_metrics_snapshot", &json_data, None)
104            .await;
105
106        Ok(())
107    }
108
109    /// Get current metrics
110    pub fn get_current_metrics(&self) -> &HashMap<String, f64> {
111        &self.current_metrics
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_metrics_config_default() {
121        let config = MetricsConfig::default();
122        assert_eq!(config.collection_interval, 60);
123        assert_eq!(config.retention_days, 90);
124        assert!(config.detailed_metrics);
125        assert!(!config.performance_profiling);
126    }
127
128    #[test]
129    fn test_metrics_collector_starts_empty() {
130        let collector = MetricsCollector::new(
131            MetricsConfig::default(),
132            std::sync::Arc::new(crate::storage::MemoryStorage::new()),
133        );
134        assert!(collector.get_current_metrics().is_empty());
135    }
136
137    #[tokio::test]
138    async fn test_collect_metrics_no_op_succeeds() {
139        let mut collector = MetricsCollector::new(
140            MetricsConfig::default(),
141            std::sync::Arc::new(crate::storage::MemoryStorage::new()),
142        );
143        let result = collector.collect_metrics(&[]).await;
144        assert!(result.is_ok());
145        assert!(collector.get_current_metrics().is_empty());
146    }
147}