1use std::collections::HashMap;
7use std::sync::Arc;
8use tokio::sync::{Mutex, mpsc};
9use async_trait::async_trait;
10use serde::{Deserialize, Serialize};
11use anyhow::{Result, Context};
12use tracing::{info, warn, error, instrument};
13use dashmap::DashMap;
14use bincode;
15use uuid::Uuid;
16use chrono::{DateTime, Utc};
17
18use kotoba_storage::KeyValueStore;
19
20pub mod event;
22pub use event::*;
23
24pub mod storage;
26pub use storage::*;
27
28pub use storage::{EventStorage, TopicMetadata};
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct EventStreamConfig {
34 pub storage_prefix: String,
36 pub max_topics: usize,
38 pub max_batch_size: usize,
40 pub retention_hours: u64,
42 pub enable_compression: bool,
44 pub enable_metrics: bool,
46}
47
48impl Default for EventStreamConfig {
49 fn default() -> Self {
50 Self {
51 storage_prefix: "events".to_string(),
52 max_topics: 100,
53 max_batch_size: 1000,
54 retention_hours: 168, enable_compression: true,
56 enable_metrics: true,
57 }
58 }
59}
60
61#[async_trait]
63pub trait EventStreamPort {
64 async fn publish(&self, event: EventEnvelope) -> Result<EventId>;
66
67 async fn subscribe(&self, topic: &str, handler: EventHandler) -> Result<()>;
69
70 async fn get_event(&self, event_id: &EventId) -> Result<Option<EventEnvelope>>;
72
73 async fn get_events_by_aggregate(&self, aggregate_id: &AggregateId) -> Result<Vec<EventEnvelope>>;
75
76 async fn create_topic(&self, topic: &str) -> Result<()>;
78
79 async fn delete_topic(&self, topic: &str) -> Result<()>;
81
82 async fn get_topic_stats(&self, topic: &str) -> Result<TopicStats>;
84
85 async fn list_topics(&self) -> Result<Vec<String>>;
87}
88
89pub type EventHandler = Box<dyn Fn(EventEnvelope) -> Result<()> + Send + Sync>;
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct TopicStats {
95 pub topic_name: String,
96 pub event_count: u64,
97 pub first_offset: u64,
98 pub last_offset: u64,
99 pub created_at: DateTime<Utc>,
100 pub size_bytes: u64,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct ConsumerOffset {
106 pub consumer_group: String,
107 pub topic: String,
108 pub partition: u32,
109 pub offset: u64,
110 pub last_updated: DateTime<Utc>,
111}
112
113pub struct EventStream<T: KeyValueStore> {
115 config: EventStreamConfig,
116 storage: Arc<EventStorage<T>>,
117 subscribers: Arc<DashMap<String, Vec<EventHandler>>>,
118 consumer_offsets: Arc<DashMap<String, ConsumerOffset>>,
119}
120
121impl<T: KeyValueStore> EventStream<T> {
122 pub fn new(config: EventStreamConfig, storage: Arc<T>) -> Self {
124 info!("Created event stream with storage backend");
125
126 Self {
127 config: config.clone(),
128 storage: Arc::new(EventStorage::new(
129 storage,
130 config.storage_prefix,
131 config.max_topics
132 )),
133 subscribers: Arc::new(DashMap::new()),
134 consumer_offsets: Arc::new(DashMap::new()),
135 }
136 }
137
138 fn validate_topic_name(&self, topic: &str) -> Result<String> {
140 if topic.is_empty() {
141 return Err(anyhow::anyhow!("Topic name cannot be empty"));
142 }
143 if topic.len() > 255 {
144 return Err(anyhow::anyhow!("Topic name too long"));
145 }
146 Ok(topic.to_string())
147 }
148}
149
150#[async_trait]
151impl<T: KeyValueStore> EventStreamPort for EventStream<T> {
152 async fn publish(&self, event: EventEnvelope) -> Result<EventId> {
153 let topic = "all".to_string();
155
156 self.storage.store_event(&topic, &event).await?;
158
159 if let Some(handlers) = self.subscribers.get(&topic) {
161 for handler in handlers.iter() {
162 if let Err(e) = handler(event.clone()) {
163 error!("Event handler error: {}", e);
164 }
165 }
166 }
167
168 info!("Published event: {} to topic: {}", event.id.0, topic);
169 Ok(event.id)
170 }
171
172 async fn subscribe(&self, topic: &str, handler: EventHandler) -> Result<()> {
173 let topic_name = self.validate_topic_name(topic)?;
174
175 self.subscribers
177 .entry(topic_name.clone())
178 .or_insert_with(Vec::new)
179 .push(handler);
180
181 info!("Subscribed to topic: {}", topic_name);
182 Ok(())
183 }
184
185 async fn get_event(&self, event_id: &EventId) -> Result<Option<EventEnvelope>> {
186 self.storage.get_event(event_id).await
187 }
188
189 async fn get_events_by_aggregate(&self, aggregate_id: &AggregateId) -> Result<Vec<EventEnvelope>> {
190 warn!("get_events_by_aggregate not fully implemented yet");
193 Ok(Vec::new())
194 }
195
196 async fn create_topic(&self, topic: &str) -> Result<()> {
197 let topic_name = self.validate_topic_name(topic)?;
198 self.storage.create_topic(&topic_name).await?;
199 info!("Created topic: {}", topic_name);
200 Ok(())
201 }
202
203 async fn delete_topic(&self, topic: &str) -> Result<()> {
204 let topic_name = self.validate_topic_name(topic)?;
205 self.storage.delete_topic(&topic_name).await?;
206 self.subscribers.remove(&topic_name);
207 info!("Deleted topic: {}", topic_name);
208 Ok(())
209 }
210
211 async fn get_topic_stats(&self, topic: &str) -> Result<TopicStats> {
212 let topic_name = self.validate_topic_name(topic)?;
213 self.storage.get_topic_stats(&topic_name).await
214 }
215
216 async fn list_topics(&self) -> Result<Vec<String>> {
217 self.storage.list_topics().await
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use std::sync::Arc;
225 use tokio::sync::mpsc;
226 use chrono::Utc;
227
228 struct MockKeyValueStore {
230 data: HashMap<Vec<u8>, Vec<u8>>,
231 }
232
233 impl MockKeyValueStore {
234 fn new() -> Self {
235 Self {
236 data: HashMap::new(),
237 }
238 }
239 }
240
241 #[async_trait::async_trait]
242 impl KeyValueStore for MockKeyValueStore {
243 async fn put(&self, key: &[u8], value: &[u8]) -> anyhow::Result<()> {
244 Ok(())
245 }
246
247 async fn get(&self, key: &[u8]) -> anyhow::Result<Option<Vec<u8>>> {
248 Ok(None)
249 }
250
251 async fn delete(&self, key: &[u8]) -> anyhow::Result<()> {
252 Ok(())
253 }
254
255 async fn scan(&self, prefix: &[u8]) -> anyhow::Result<Vec<(Vec<u8>, Vec<u8>)>> {
256 Ok(vec![])
257 }
258 }
259
260 #[test]
261 fn test_event_stream_config_creation() {
262 let config = EventStreamConfig {
263 storage_prefix: "test_events".to_string(),
264 max_topics: 50,
265 max_batch_size: 500,
266 retention_hours: 24,
267 enable_compression: false,
268 enable_metrics: false,
269 };
270
271 assert_eq!(config.storage_prefix, "test_events");
272 assert_eq!(config.max_topics, 50);
273 assert_eq!(config.max_batch_size, 500);
274 assert_eq!(config.retention_hours, 24);
275 assert!(!config.enable_compression);
276 assert!(!config.enable_metrics);
277 }
278
279 #[test]
280 fn test_event_stream_config_default() {
281 let config = EventStreamConfig::default();
282
283 assert_eq!(config.storage_prefix, "events");
284 assert_eq!(config.max_topics, 100);
285 assert_eq!(config.max_batch_size, 1000);
286 assert_eq!(config.retention_hours, 168); assert!(config.enable_compression);
288 assert!(config.enable_metrics);
289 }
290
291 #[test]
292 fn test_event_stream_config_clone() {
293 let original = EventStreamConfig::default();
294 let cloned = original.clone();
295
296 assert_eq!(original.storage_prefix, cloned.storage_prefix);
297 assert_eq!(original.max_topics, cloned.max_topics);
298 assert_eq!(original.max_batch_size, cloned.max_batch_size);
299 assert_eq!(original.retention_hours, cloned.retention_hours);
300 assert_eq!(original.enable_compression, cloned.enable_compression);
301 assert_eq!(original.enable_metrics, cloned.enable_metrics);
302 }
303
304 #[test]
305 fn test_event_stream_config_debug() {
306 let config = EventStreamConfig::default();
307 let debug_str = format!("{:?}", config);
308 assert!(debug_str.contains("events"));
309 assert!(debug_str.contains("100"));
310 assert!(debug_str.contains("1000"));
311 assert!(debug_str.contains("168"));
312 }
313
314 #[test]
315 fn test_event_stream_config_serialization() {
316 let config = EventStreamConfig {
317 storage_prefix: "test_prefix".to_string(),
318 max_topics: 200,
319 max_batch_size: 2000,
320 retention_hours: 336, enable_compression: false,
322 enable_metrics: true,
323 };
324
325 let json_result = serde_json::to_string(&config);
327 assert!(json_result.is_ok());
328
329 let json_str = json_result.unwrap();
330 assert!(json_str.contains("test_prefix"));
331 assert!(json_str.contains("200"));
332 assert!(json_str.contains("2000"));
333 assert!(json_str.contains("336"));
334
335 let deserialized_result: serde_json::Result<EventStreamConfig> = serde_json::from_str(&json_str);
337 assert!(deserialized_result.is_ok());
338
339 let deserialized = deserialized_result.unwrap();
340 assert_eq!(deserialized.storage_prefix, "test_prefix");
341 assert_eq!(deserialized.max_topics, 200);
342 assert_eq!(deserialized.max_batch_size, 2000);
343 assert_eq!(deserialized.retention_hours, 336);
344 assert!(!deserialized.enable_compression);
345 assert!(deserialized.enable_metrics);
346 }
347
348 #[test]
349 fn test_topic_stats_creation() {
350 let stats = TopicStats {
351 topic_name: "test_topic".to_string(),
352 event_count: 1000,
353 first_offset: 0,
354 last_offset: 999,
355 created_at: Utc::now(),
356 size_bytes: 1048576, };
358
359 assert_eq!(stats.topic_name, "test_topic");
360 assert_eq!(stats.event_count, 1000);
361 assert_eq!(stats.first_offset, 0);
362 assert_eq!(stats.last_offset, 999);
363 assert_eq!(stats.size_bytes, 1048576);
364 }
365
366 #[test]
367 fn test_topic_stats_serialization() {
368 let now = Utc::now();
369 let stats = TopicStats {
370 topic_name: "serialization_test".to_string(),
371 event_count: 500,
372 first_offset: 100,
373 last_offset: 599,
374 created_at: now,
375 size_bytes: 524288, };
377
378 let json_result = serde_json::to_string(&stats);
380 assert!(json_result.is_ok());
381
382 let json_str = json_result.unwrap();
383 assert!(json_str.contains("serialization_test"));
384 assert!(json_str.contains("500"));
385 assert!(json_str.contains("100"));
386 assert!(json_str.contains("599"));
387 assert!(json_str.contains("524288"));
388
389 let deserialized_result: serde_json::Result<TopicStats> = serde_json::from_str(&json_str);
391 assert!(deserialized_result.is_ok());
392
393 let deserialized = deserialized_result.unwrap();
394 assert_eq!(deserialized.topic_name, "serialization_test");
395 assert_eq!(deserialized.event_count, 500);
396 assert_eq!(deserialized.first_offset, 100);
397 assert_eq!(deserialized.last_offset, 599);
398 assert_eq!(deserialized.size_bytes, 524288);
399 }
400
401 #[test]
402 fn test_consumer_offset_creation() {
403 let now = Utc::now();
404 let offset = ConsumerOffset {
405 consumer_group: "test_group".to_string(),
406 topic: "test_topic".to_string(),
407 partition: 0,
408 offset: 1000,
409 last_updated: now,
410 };
411
412 assert_eq!(offset.consumer_group, "test_group");
413 assert_eq!(offset.topic, "test_topic");
414 assert_eq!(offset.partition, 0);
415 assert_eq!(offset.offset, 1000);
416 }
417
418 #[test]
419 fn test_consumer_offset_serialization() {
420 let now = Utc::now();
421 let offset = ConsumerOffset {
422 consumer_group: "group_001".to_string(),
423 topic: "topic_001".to_string(),
424 partition: 2,
425 offset: 5000,
426 last_updated: now,
427 };
428
429 let json_result = serde_json::to_string(&offset);
431 assert!(json_result.is_ok());
432
433 let json_str = json_result.unwrap();
434 assert!(json_str.contains("group_001"));
435 assert!(json_str.contains("topic_001"));
436 assert!(json_str.contains("2"));
437 assert!(json_str.contains("5000"));
438
439 let deserialized_result: serde_json::Result<ConsumerOffset> = serde_json::from_str(&json_str);
441 assert!(deserialized_result.is_ok());
442
443 let deserialized = deserialized_result.unwrap();
444 assert_eq!(deserialized.consumer_group, "group_001");
445 assert_eq!(deserialized.topic, "topic_001");
446 assert_eq!(deserialized.partition, 2);
447 assert_eq!(deserialized.offset, 5000);
448 }
449
450 #[test]
451 fn test_event_handler_type() {
452 let handler: EventHandler = Box::new(|_event| Ok(()));
454 assert!(handler.is_send());
455 assert!(handler.is_sync());
456 }
457
458 #[tokio::test]
459 async fn test_event_stream_creation() {
460 let config = EventStreamConfig::default();
461 let storage = Arc::new(MockKeyValueStore::new());
462 let event_stream = EventStream::new(config, storage);
463
464 assert_eq!(event_stream.config.storage_prefix, "events");
466 assert_eq!(event_stream.config.max_topics, 100);
467 }
468
469 #[test]
470 fn test_validate_topic_name() {
471 let config = EventStreamConfig::default();
472 let storage = Arc::new(MockKeyValueStore::new());
473 let event_stream = EventStream::new(config, storage);
474
475 assert!(event_stream.validate_topic_name("valid_topic").is_ok());
477 assert!(event_stream.validate_topic_name("topic.with.dots").is_ok());
478 assert!(event_stream.validate_topic_name("topic-with-dashes").is_ok());
479
480 assert!(event_stream.validate_topic_name("").is_err());
482 assert!(event_stream.validate_topic_name(&"a".repeat(256)).is_err());
483 }
484
485 #[tokio::test]
486 async fn test_event_stream_port_publish() {
487 let config = EventStreamConfig::default();
488 let storage = Arc::new(MockKeyValueStore::new());
489 let event_stream = EventStream::new(config, storage);
490
491 let event_data = serde_json::json!({"type": "test_event", "value": 42});
493 let event = EventEnvelope {
494 id: EventId(Uuid::new_v4()),
495 aggregate_id: AggregateId(Uuid::new_v4()),
496 event_type: "TestEvent".to_string(),
497 data: event_data,
498 metadata: HashMap::new(),
499 timestamp: Utc::now(),
500 version: 1,
501 };
502
503 let result = event_stream.publish(event.clone()).await;
505 assert!(result.is_ok());
506
507 let published_event_id = result.unwrap();
508 assert_eq!(published_event_id, event.id);
509 }
510
511 #[tokio::test]
512 async fn test_event_stream_port_subscribe() {
513 let config = EventStreamConfig::default();
514 let storage = Arc::new(MockKeyValueStore::new());
515 let event_stream = EventStream::new(config, storage);
516
517 let handler: EventHandler = Box::new(|event| {
519 println!("Received event: {}", event.id.0);
520 Ok(())
521 });
522
523 let result = event_stream.subscribe("test_topic", handler).await;
525 assert!(result.is_ok());
526
527 assert!(event_stream.subscribers.contains_key("test_topic"));
529 }
530
531 #[tokio::test]
532 async fn test_event_stream_port_get_event() {
533 let config = EventStreamConfig::default();
534 let storage = Arc::new(MockKeyValueStore::new());
535 let event_stream = EventStream::new(config, storage);
536
537 let event_id = EventId(Uuid::new_v4());
538
539 let result = event_stream.get_event(&event_id).await;
541 assert!(result.is_ok());
542 assert!(result.unwrap().is_none());
543 }
544
545 #[tokio::test]
546 async fn test_event_stream_port_get_events_by_aggregate() {
547 let config = EventStreamConfig::default();
548 let storage = Arc::new(MockKeyValueStore::new());
549 let event_stream = EventStream::new(config, storage);
550
551 let aggregate_id = AggregateId(Uuid::new_v4());
552
553 let result = event_stream.get_events_by_aggregate(&aggregate_id).await;
555 assert!(result.is_ok());
556 assert!(result.unwrap().is_empty());
557 }
558
559 #[tokio::test]
560 async fn test_event_stream_port_create_topic() {
561 let config = EventStreamConfig::default();
562 let storage = Arc::new(MockKeyValueStore::new());
563 let event_stream = EventStream::new(config, storage);
564
565 let result = event_stream.create_topic("new_topic").await;
567 assert!(result.is_ok() || result.is_err()); }
570
571 #[tokio::test]
572 async fn test_event_stream_port_delete_topic() {
573 let config = EventStreamConfig::default();
574 let storage = Arc::new(MockKeyValueStore::new());
575 let event_stream = EventStream::new(config, storage);
576
577 let result = event_stream.delete_topic("test_topic").await;
579 assert!(result.is_ok() || result.is_err()); assert!(!event_stream.subscribers.contains_key("test_topic"));
584 }
585
586 #[tokio::test]
587 async fn test_event_stream_port_list_topics() {
588 let config = EventStreamConfig::default();
589 let storage = Arc::new(MockKeyValueStore::new());
590 let event_stream = EventStream::new(config, storage);
591
592 let result = event_stream.list_topics().await;
594 assert!(result.is_ok() || result.is_err()); }
597
598 #[tokio::test]
599 async fn test_event_stream_port_get_topic_stats() {
600 let config = EventStreamConfig::default();
601 let storage = Arc::new(MockKeyValueStore::new());
602 let event_stream = EventStream::new(config, storage);
603
604 let result = event_stream.get_topic_stats("test_topic").await;
606 assert!(result.is_ok() || result.is_err()); }
609
610 #[tokio::test]
611 async fn test_event_stream_publish_with_subscribers() {
612 let config = EventStreamConfig::default();
613 let storage = Arc::new(MockKeyValueStore::new());
614 let event_stream = EventStream::new(config, storage);
615
616 let (tx, mut rx) = mpsc::channel(1);
618
619 let handler: EventHandler = Box::new(move |event| {
621 let tx = tx.clone();
622 tokio::spawn(async move {
623 let _ = tx.send(event.id.0).await;
624 });
625 Ok(())
626 });
627
628 event_stream.subscribe("all", handler).await.unwrap();
630
631 let event_data = serde_json::json!({"type": "notification", "message": "test"});
633 let event = EventEnvelope {
634 id: EventId(Uuid::new_v4()),
635 aggregate_id: AggregateId(Uuid::new_v4()),
636 event_type: "NotificationEvent".to_string(),
637 data: event_data,
638 metadata: HashMap::new(),
639 timestamp: Utc::now(),
640 version: 1,
641 };
642
643 let event_id = event_stream.publish(event.clone()).await.unwrap();
644
645 tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
647
648 match tokio::time::timeout(tokio::time::Duration::from_millis(100), rx.recv()).await {
650 Ok(Some(received_id)) => assert_eq!(received_id, event_id.0),
651 _ => {} }
653 }
654
655 #[test]
656 fn test_event_stream_config_edge_cases() {
657 let config = EventStreamConfig {
659 storage_prefix: "".to_string(), max_topics: 0, max_batch_size: 1, retention_hours: 0, enable_compression: false,
664 enable_metrics: false,
665 };
666
667 assert_eq!(config.storage_prefix, "");
668 assert_eq!(config.max_topics, 0);
669 assert_eq!(config.max_batch_size, 1);
670 assert_eq!(config.retention_hours, 0);
671 }
672
673 #[tokio::test]
674 async fn test_event_stream_multiple_subscribers() {
675 let config = EventStreamConfig::default();
676 let storage = Arc::new(MockKeyValueStore::new());
677 let event_stream = EventStream::new(config, storage);
678
679 let (tx1, _rx1) = mpsc::channel(1);
680 let (tx2, _rx2) = mpsc::channel(1);
681
682 let handler1: EventHandler = Box::new(move |_| Ok(()));
684 let handler2: EventHandler = Box::new(move |_| Ok(()));
685
686 event_stream.subscribe("multi_topic", handler1).await.unwrap();
687 event_stream.subscribe("multi_topic", handler2).await.unwrap();
688
689 if let Some(handlers) = event_stream.subscribers.get("multi_topic") {
691 assert_eq!(handlers.len(), 2);
692 }
693 }
694
695 #[test]
696 fn test_topic_stats_calculations() {
697 let now = Utc::now();
698 let stats = TopicStats {
699 topic_name: "calc_test".to_string(),
700 event_count: 1000,
701 first_offset: 0,
702 last_offset: 999,
703 created_at: now,
704 size_bytes: 1048576,
705 };
706
707 assert_eq!(stats.event_count, 1000);
709 assert_eq!(stats.last_offset - stats.first_offset + 1, 1000);
710 assert_eq!(stats.size_bytes, 1048576);
711 }
712
713 #[test]
714 fn test_consumer_offset_updates() {
715 let now = Utc::now();
716 let mut offset = ConsumerOffset {
717 consumer_group: "test_group".to_string(),
718 topic: "test_topic".to_string(),
719 partition: 0,
720 offset: 1000,
721 last_updated: now,
722 };
723
724 offset.offset = 1500;
726 offset.last_updated = Utc::now();
727
728 assert_eq!(offset.offset, 1500);
729 assert!(offset.last_updated >= now);
730 }
731}