allsource_core/application/use_cases/
query_events.rs1use crate::{
2 application::dto::{EventDto, QueryEventsRequest, QueryEventsResponse},
3 domain::repositories::EventRepository,
4 error::Result,
5};
6use std::sync::Arc;
7
8pub struct QueryEventsUseCase {
19 repository: Arc<dyn EventRepository>,
20}
21
22impl QueryEventsUseCase {
23 pub fn new(repository: Arc<dyn EventRepository>) -> Self {
24 Self { repository }
25 }
26
27 pub async fn execute(&self, request: QueryEventsRequest) -> Result<QueryEventsResponse> {
28 let tenant_id = request.tenant_id.unwrap_or_else(|| "default".to_string());
30
31 let mut events = if let Some(entity_id) = request.entity_id {
33 if let Some(as_of) = request.as_of {
35 self.repository
37 .find_by_entity_as_of(&entity_id, &tenant_id, as_of)
38 .await?
39 } else {
40 self.repository
41 .find_by_entity(&entity_id, &tenant_id)
42 .await?
43 }
44 } else if let Some(event_type) = request.event_type {
45 self.repository
47 .find_by_type(&event_type, &tenant_id)
48 .await?
49 } else if let (Some(since), Some(until)) = (request.since, request.until) {
50 self.repository
52 .find_by_time_range(&tenant_id, since, until)
53 .await?
54 } else {
55 return Err(crate::error::Error::InvalidInput(
58 "Query requires at least one filter (entity_id, event_type, or time range)"
59 .to_string(),
60 ));
61 };
62
63 if let Some(since) = request.since {
65 events.retain(|e| e.occurred_after(since));
66 }
67 if let Some(until) = request.until {
68 events.retain(|e| e.occurred_before(until));
69 }
70
71 let total_count = events.len();
73
74 if let Some(limit) = request.limit {
76 events.truncate(limit);
77 }
78
79 let event_dtos: Vec<EventDto> = events.iter().map(EventDto::from).collect();
81 let count = event_dtos.len();
82 let has_more = count < total_count;
83
84 Ok(QueryEventsResponse {
85 events: event_dtos,
86 count,
87 total_count,
88 has_more,
89 entity_version: None,
90 })
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate::domain::entities::Event;
98 use async_trait::async_trait;
99 use chrono::Utc;
100 use serde_json::json;
101 use uuid::Uuid;
102
103 struct MockEventRepository {
105 events: Vec<Event>,
106 }
107
108 impl MockEventRepository {
109 fn with_events(events: Vec<Event>) -> Self {
110 Self { events }
111 }
112 }
113
114 #[async_trait]
115 impl EventRepository for MockEventRepository {
116 async fn save(&self, _event: &Event) -> Result<()> {
117 Ok(())
118 }
119
120 async fn save_batch(&self, _events: &[Event]) -> Result<()> {
121 Ok(())
122 }
123
124 async fn find_by_id(&self, id: Uuid) -> Result<Option<Event>> {
125 Ok(self.events.iter().find(|e| e.id() == id).cloned())
126 }
127
128 async fn find_by_entity(&self, entity_id: &str, tenant_id: &str) -> Result<Vec<Event>> {
129 Ok(self
130 .events
131 .iter()
132 .filter(|e| e.entity_id_str() == entity_id && e.tenant_id_str() == tenant_id)
133 .cloned()
134 .collect())
135 }
136
137 async fn find_by_type(&self, event_type: &str, tenant_id: &str) -> Result<Vec<Event>> {
138 Ok(self
139 .events
140 .iter()
141 .filter(|e| e.event_type_str() == event_type && e.tenant_id_str() == tenant_id)
142 .cloned()
143 .collect())
144 }
145
146 async fn find_by_time_range(
147 &self,
148 tenant_id: &str,
149 start: chrono::DateTime<Utc>,
150 end: chrono::DateTime<Utc>,
151 ) -> Result<Vec<Event>> {
152 Ok(self
153 .events
154 .iter()
155 .filter(|e| e.tenant_id_str() == tenant_id && e.occurred_between(start, end))
156 .cloned()
157 .collect())
158 }
159
160 async fn find_by_entity_as_of(
161 &self,
162 entity_id: &str,
163 tenant_id: &str,
164 as_of: chrono::DateTime<Utc>,
165 ) -> Result<Vec<Event>> {
166 Ok(self
167 .events
168 .iter()
169 .filter(|e| {
170 e.entity_id_str() == entity_id
171 && e.tenant_id_str() == tenant_id
172 && e.occurred_before(as_of)
173 })
174 .cloned()
175 .collect())
176 }
177
178 async fn count(&self, tenant_id: &str) -> Result<usize> {
179 Ok(self
180 .events
181 .iter()
182 .filter(|e| e.tenant_id_str() == tenant_id)
183 .count())
184 }
185
186 async fn health_check(&self) -> Result<()> {
187 Ok(())
188 }
189 }
190
191 fn create_test_events() -> Vec<Event> {
192 vec![
193 Event::from_strings(
194 "user.created".to_string(),
195 "user-1".to_string(),
196 "tenant-1".to_string(),
197 json!({"name": "Alice"}),
198 None,
199 )
200 .unwrap(),
201 Event::from_strings(
202 "user.created".to_string(),
203 "user-2".to_string(),
204 "tenant-1".to_string(),
205 json!({"name": "Bob"}),
206 None,
207 )
208 .unwrap(),
209 Event::from_strings(
210 "order.placed".to_string(),
211 "order-1".to_string(),
212 "tenant-1".to_string(),
213 json!({"amount": 100}),
214 None,
215 )
216 .unwrap(),
217 ]
218 }
219
220 #[tokio::test]
221 async fn test_query_by_entity() {
222 let events = create_test_events();
223 let entity_id = events[0].entity_id().to_string();
224 let repo = Arc::new(MockEventRepository::with_events(events));
225 let use_case = QueryEventsUseCase::new(repo);
226
227 let request = QueryEventsRequest {
228 entity_id: Some(entity_id),
229 event_type: None,
230 tenant_id: Some("tenant-1".to_string()),
231 as_of: None,
232 since: None,
233 until: None,
234 limit: None,
235 event_type_prefix: None,
236 exclude_event_type_prefix: None,
237 payload_filter: None,
238 };
239
240 let response = use_case.execute(request).await;
241 assert!(response.is_ok());
242
243 let response = response.unwrap();
244 assert_eq!(response.count, 1);
245 }
246
247 #[tokio::test]
248 async fn test_query_by_type() {
249 let events = create_test_events();
250 let repo = Arc::new(MockEventRepository::with_events(events));
251 let use_case = QueryEventsUseCase::new(repo);
252
253 let request = QueryEventsRequest {
254 entity_id: None,
255 event_type: Some("user.created".to_string()),
256 tenant_id: Some("tenant-1".to_string()),
257 as_of: None,
258 since: None,
259 until: None,
260 limit: None,
261 event_type_prefix: None,
262 exclude_event_type_prefix: None,
263 payload_filter: None,
264 };
265
266 let response = use_case.execute(request).await;
267 assert!(response.is_ok());
268
269 let response = response.unwrap();
270 assert_eq!(response.count, 2);
271 }
272
273 #[tokio::test]
274 async fn test_query_with_limit() {
275 let events = create_test_events();
276 let repo = Arc::new(MockEventRepository::with_events(events));
277 let use_case = QueryEventsUseCase::new(repo);
278
279 let request = QueryEventsRequest {
280 entity_id: None,
281 event_type: Some("user.created".to_string()),
282 tenant_id: Some("tenant-1".to_string()),
283 as_of: None,
284 since: None,
285 until: None,
286 limit: Some(1),
287 event_type_prefix: None,
288 exclude_event_type_prefix: None,
289 payload_filter: None,
290 };
291
292 let response = use_case.execute(request).await;
293 assert!(response.is_ok());
294
295 let response = response.unwrap();
296 assert_eq!(response.count, 1);
297 }
298
299 #[tokio::test]
300 async fn test_query_requires_filter() {
301 let events = create_test_events();
302 let repo = Arc::new(MockEventRepository::with_events(events));
303 let use_case = QueryEventsUseCase::new(repo);
304
305 let request = QueryEventsRequest {
306 entity_id: None,
307 event_type: None,
308 tenant_id: Some("tenant-1".to_string()),
309 as_of: None,
310 since: None,
311 until: None,
312 limit: None,
313 event_type_prefix: None,
314 exclude_event_type_prefix: None,
315 payload_filter: None,
316 };
317
318 let response = use_case.execute(request).await;
319 assert!(response.is_err());
320 }
321}