auth_framework/analytics/
metrics.rs1use super::{AnalyticsError, AnalyticsEvent};
10use crate::storage::AuthStorage;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::sync::Arc;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct MetricsConfig {
18 pub collection_interval: u64,
20
21 pub retention_days: u32,
23
24 pub detailed_metrics: bool,
26
27 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
42pub struct MetricsCollector {
44 _config: MetricsConfig,
45 storage: Arc<dyn AuthStorage>,
46 current_metrics: HashMap<String, f64>,
47}
48
49impl MetricsCollector {
50 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 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 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}