1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use uuid::Uuid;
10
11use crate::schemas::events::{EventType, Severity, SourceModule};
12
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
15pub struct CorrelationId(pub Uuid);
16
17impl CorrelationId {
18 pub fn new() -> Self {
19 Self(Uuid::new_v4())
20 }
21}
22
23impl Default for CorrelationId {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct EventCorrelation {
32 pub correlation_id: CorrelationId,
34
35 pub correlation_type: CorrelationType,
37
38 pub events: Vec<CorrelatedEvent>,
40
41 pub strength: f64,
43
44 pub confidence: f64,
46
47 pub time_window: TimeWindow,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub pattern: Option<CorrelationPattern>,
53
54 pub detected_at: DateTime<Utc>,
56
57 #[serde(default)]
59 pub metadata: HashMap<String, String>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct TimeWindow {
64 pub start: DateTime<Utc>,
65 pub end: DateTime<Utc>,
66}
67
68impl TimeWindow {
69 pub fn duration_seconds(&self) -> i64 {
70 (self.end - self.start).num_seconds()
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
76#[serde(rename_all = "snake_case")]
77pub enum CorrelationType {
78 CausalChain,
80
81 Temporal,
83
84 PatternMatch,
86
87 Anomaly,
89
90 CostImpact,
92
93 SecurityIncident,
95
96 PerformanceDegradation,
98
99 ComplianceCascade,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct CorrelatedEvent {
106 pub event_id: Uuid,
108
109 pub source_module: SourceModule,
111
112 pub event_type: EventType,
114
115 pub severity: Severity,
117
118 pub timestamp: DateTime<Utc>,
120
121 pub role: EventRole,
123
124 pub summary: String,
126
127 #[serde(default)]
129 pub metrics: HashMap<String, f64>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
133#[serde(rename_all = "snake_case")]
134pub enum EventRole {
135 RootCause,
137
138 Contributor,
140
141 Effect,
143
144 Related,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct CorrelationPattern {
151 pub pattern_id: String,
153
154 pub name: String,
156
157 pub description: String,
159
160 pub modules: Vec<SourceModule>,
162
163 pub sequence: Vec<PatternStep>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct PatternStep {
169 pub step_number: u32,
170 pub module: SourceModule,
171 pub event_type: EventType,
172 pub time_offset_ms: Option<i64>, pub conditions: HashMap<String, String>,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct AnomalyCorrelation {
179 pub correlation_id: CorrelationId,
181
182 pub anomalies: Vec<AnomalyEvent>,
184
185 pub strength: f64,
187
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub root_cause: Option<RootCauseAnalysis>,
191
192 pub impact: ImpactAssessment,
194
195 pub detected_at: DateTime<Utc>,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct AnomalyEvent {
201 pub event_id: Uuid,
203
204 pub source_module: SourceModule,
206
207 pub anomaly_type: AnomalyType,
209
210 pub anomaly_score: f64,
212
213 pub baseline: f64,
215
216 pub observed: f64,
218
219 pub deviation: f64,
221
222 pub timestamp: DateTime<Utc>,
224
225 pub metric: String,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
230#[serde(rename_all = "snake_case")]
231pub enum AnomalyType {
232 Spike,
234
235 Drop,
237
238 PatternDeviation,
240
241 FrequencyAnomaly,
243
244 DistributionShift,
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct RootCauseAnalysis {
251 pub root_event_id: Uuid,
253
254 pub confidence: f64,
256
257 pub causal_chain: Vec<CausalLink>,
259
260 pub contributing_factors: Vec<String>,
262
263 pub recommendations: Vec<String>,
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct CausalLink {
269 pub from_event_id: Uuid,
270 pub to_event_id: Uuid,
271 pub relationship: CausalRelationship,
272 pub strength: f64,
273 pub time_delta_ms: i64,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
277#[serde(rename_all = "snake_case")]
278pub enum CausalRelationship {
279 DirectCause,
280 IndirectCause,
281 Correlation,
282 Amplification,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct ImpactAssessment {
288 pub severity: ImpactSeverity,
290
291 pub affected_modules: Vec<SourceModule>,
293
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub performance_impact: Option<PerformanceImpact>,
297
298 #[serde(skip_serializing_if = "Option::is_none")]
300 pub cost_impact: Option<CostImpact>,
301
302 #[serde(skip_serializing_if = "Option::is_none")]
304 pub security_impact: Option<SecurityImpact>,
305
306 #[serde(skip_serializing_if = "Option::is_none")]
308 pub business_impact: Option<BusinessImpact>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
312#[serde(rename_all = "lowercase")]
313pub enum ImpactSeverity {
314 Negligible,
315 Low,
316 Medium,
317 High,
318 Critical,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct PerformanceImpact {
323 pub latency_increase_percent: f64,
324 pub throughput_decrease_percent: f64,
325 pub error_rate_increase_percent: f64,
326 pub affected_requests: u64,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct CostImpact {
331 pub additional_cost_usd: f64,
332 pub cost_increase_percent: f64,
333 pub wasted_resources_usd: f64,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct SecurityImpact {
338 pub threats_detected: u64,
339 pub vulnerabilities_exposed: u64,
340 pub data_at_risk: bool,
341 pub compliance_violations: u64,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct BusinessImpact {
346 pub users_affected: u64,
347 pub sla_violations: u64,
348 pub revenue_impact_usd: Option<f64>,
349 pub reputation_risk: ReputationRisk,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
353#[serde(rename_all = "lowercase")]
354pub enum ReputationRisk {
355 None,
356 Low,
357 Medium,
358 High,
359 Severe,
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct EventGraph {
365 pub graph_id: String,
367
368 pub time_range: TimeWindow,
370
371 pub nodes: Vec<EventNode>,
373
374 pub edges: Vec<EventEdge>,
376
377 pub metadata: GraphMetadata,
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct EventNode {
383 pub node_id: String,
384 pub event_id: Uuid,
385 pub source_module: SourceModule,
386 pub event_type: EventType,
387 pub timestamp: DateTime<Utc>,
388 pub attributes: HashMap<String, String>,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
392pub struct EventEdge {
393 pub edge_id: String,
394 pub from_node: String,
395 pub to_node: String,
396 pub relationship_type: EdgeRelationship,
397 pub weight: f64,
398 pub properties: HashMap<String, String>,
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
402#[serde(rename_all = "snake_case")]
403pub enum EdgeRelationship {
404 Causes,
405 TriggeredBy,
406 RelatedTo,
407 Precedes,
408 Follows,
409 CorrelatesWith,
410 Amplifies,
411 Mitigates,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct GraphMetadata {
416 pub node_count: usize,
417 pub edge_count: usize,
418 pub connected_components: usize,
419 pub avg_degree: f64,
420 pub density: f64,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct CorrelationQuery {
426 pub seed_event_id: Uuid,
428
429 pub time_window_minutes: i64,
431
432 #[serde(default = "default_min_strength")]
434 pub min_strength: f64,
435
436 #[serde(default)]
438 pub correlation_types: Vec<CorrelationType>,
439
440 #[serde(default)]
442 pub include_modules: Vec<SourceModule>,
443
444 #[serde(default = "default_max_depth")]
446 pub max_depth: u32,
447}
448
449fn default_min_strength() -> f64 {
450 0.7
451}
452
453fn default_max_depth() -> u32 {
454 5
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct CorrelationConfig {
460 pub auto_detect: bool,
462
463 pub patterns: Vec<CorrelationPattern>,
465
466 pub correlation_window_minutes: i64,
468
469 pub min_events: usize,
471
472 pub alert_thresholds: AlertThresholds,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct AlertThresholds {
478 pub min_correlation_strength: f64,
479 pub min_anomaly_score: f64,
480 pub critical_impact_threshold: f64,
481}
482
483impl Default for CorrelationConfig {
484 fn default() -> Self {
485 Self {
486 auto_detect: true,
487 patterns: Vec::new(),
488 correlation_window_minutes: 60,
489 min_events: 2,
490 alert_thresholds: AlertThresholds {
491 min_correlation_strength: 0.8,
492 min_anomaly_score: 0.7,
493 critical_impact_threshold: 0.9,
494 },
495 }
496 }
497}
498
499#[cfg(test)]
500mod tests {
501 use super::*;
502
503 #[test]
504 fn test_correlation_id_creation() {
505 let id1 = CorrelationId::new();
506 let id2 = CorrelationId::new();
507 assert_ne!(id1, id2);
508 }
509
510 #[test]
511 fn test_time_window_duration() {
512 let start = Utc::now();
513 let end = start + chrono::Duration::minutes(30);
514 let window = TimeWindow { start, end };
515 assert_eq!(window.duration_seconds(), 1800);
516 }
517
518 #[test]
519 fn test_event_correlation_serialization() {
520 let correlation = EventCorrelation {
521 correlation_id: CorrelationId::new(),
522 correlation_type: CorrelationType::CausalChain,
523 events: vec![],
524 strength: 0.85,
525 confidence: 0.9,
526 time_window: TimeWindow {
527 start: Utc::now(),
528 end: Utc::now() + chrono::Duration::minutes(10),
529 },
530 pattern: None,
531 detected_at: Utc::now(),
532 metadata: HashMap::new(),
533 };
534
535 let json = serde_json::to_string_pretty(&correlation).unwrap();
536 assert!(json.contains("causal_chain"));
537 assert!(json.contains("0.85"));
538 }
539}