1use async_trait::async_trait;
33use serde::{Deserialize, Serialize};
34use std::collections::HashMap;
35use std::sync::Arc;
36use tokio::sync::RwLock;
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct RequestContext {
41 pub method: String,
43 pub path: String,
45 pub headers: HashMap<String, String>,
47 pub query_params: HashMap<String, String>,
49 pub body: Option<Vec<u8>>,
51 pub request_id: String,
53 pub timestamp: chrono::DateTime<chrono::Utc>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct ResponseContext {
60 pub request: RequestContext,
62 pub status_code: u16,
64 pub headers: HashMap<String, String>,
66 pub body: Option<Vec<u8>>,
68 pub response_time_ms: u64,
70 pub timestamp: chrono::DateTime<chrono::Utc>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub enum MockLifecycleEvent {
77 Created {
79 id: String,
81 name: String,
83 config: serde_json::Value,
85 },
86 Updated {
88 id: String,
90 name: String,
92 config: serde_json::Value,
94 },
95 Deleted {
97 id: String,
99 name: String,
101 },
102 Enabled {
104 id: String,
106 },
107 Disabled {
109 id: String,
111 },
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub enum ServerLifecycleEvent {
117 Startup {
119 config: serde_json::Value,
121 },
122 Shutdown {
124 reason: String,
126 },
127}
128
129#[async_trait]
135pub trait LifecycleHook: Send + Sync {
136 async fn before_request(
145 &self,
146 _ctx: &RequestContext,
147 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
148 Ok(())
149 }
150
151 async fn after_response(
160 &self,
161 _ctx: &ResponseContext,
162 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
163 Ok(())
164 }
165
166 async fn on_mock_created(
168 &self,
169 _event: &MockLifecycleEvent,
170 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
171 Ok(())
172 }
173
174 async fn on_mock_updated(
176 &self,
177 _event: &MockLifecycleEvent,
178 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
179 Ok(())
180 }
181
182 async fn on_mock_deleted(
184 &self,
185 _event: &MockLifecycleEvent,
186 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
187 Ok(())
188 }
189
190 async fn on_mock_state_changed(
192 &self,
193 _event: &MockLifecycleEvent,
194 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
195 Ok(())
196 }
197
198 async fn on_startup(
200 &self,
201 _event: &ServerLifecycleEvent,
202 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
203 Ok(())
204 }
205
206 async fn on_shutdown(
208 &self,
209 _event: &ServerLifecycleEvent,
210 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
211 Ok(())
212 }
213}
214
215pub struct LifecycleHookRegistry {
219 hooks: Arc<RwLock<Vec<Arc<dyn LifecycleHook>>>>,
220}
221
222impl LifecycleHookRegistry {
223 pub fn new() -> Self {
225 Self {
226 hooks: Arc::new(RwLock::new(Vec::new())),
227 }
228 }
229
230 pub async fn register_hook(&self, hook: Arc<dyn LifecycleHook>) {
232 let mut hooks = self.hooks.write().await;
233 hooks.push(hook);
234 }
235
236 pub async fn invoke_before_request(&self, ctx: &RequestContext) {
238 let hooks = self.hooks.read().await;
239 for hook in hooks.iter() {
240 if let Err(e) = hook.before_request(ctx).await {
241 tracing::error!("Error in before_request hook: {}", e);
242 }
243 }
244 }
245
246 pub async fn invoke_after_response(&self, ctx: &ResponseContext) {
248 let hooks = self.hooks.read().await;
249 for hook in hooks.iter() {
250 if let Err(e) = hook.after_response(ctx).await {
251 tracing::error!("Error in after_response hook: {}", e);
252 }
253 }
254 }
255
256 pub async fn invoke_mock_created(&self, event: &MockLifecycleEvent) {
258 let hooks = self.hooks.read().await;
259 for hook in hooks.iter() {
260 if let Err(e) = hook.on_mock_created(event).await {
261 tracing::error!("Error in on_mock_created hook: {}", e);
262 }
263 }
264 }
265
266 pub async fn invoke_mock_updated(&self, event: &MockLifecycleEvent) {
268 let hooks = self.hooks.read().await;
269 for hook in hooks.iter() {
270 if let Err(e) = hook.on_mock_updated(event).await {
271 tracing::error!("Error in on_mock_updated hook: {}", e);
272 }
273 }
274 }
275
276 pub async fn invoke_mock_deleted(&self, event: &MockLifecycleEvent) {
278 let hooks = self.hooks.read().await;
279 for hook in hooks.iter() {
280 if let Err(e) = hook.on_mock_deleted(event).await {
281 tracing::error!("Error in on_mock_deleted hook: {}", e);
282 }
283 }
284 }
285
286 pub async fn invoke_mock_state_changed(&self, event: &MockLifecycleEvent) {
288 let hooks = self.hooks.read().await;
289 for hook in hooks.iter() {
290 if let Err(e) = hook.on_mock_state_changed(event).await {
291 tracing::error!("Error in on_mock_state_changed hook: {}", e);
292 }
293 }
294 }
295
296 pub async fn invoke_startup(&self, event: &ServerLifecycleEvent) {
298 let hooks = self.hooks.read().await;
299 for hook in hooks.iter() {
300 if let Err(e) = hook.on_startup(event).await {
301 tracing::error!("Error in on_startup hook: {}", e);
302 }
303 }
304 }
305
306 pub async fn invoke_shutdown(&self, event: &ServerLifecycleEvent) {
308 let hooks = self.hooks.read().await;
309 for hook in hooks.iter() {
310 if let Err(e) = hook.on_shutdown(event).await {
311 tracing::error!("Error in on_shutdown hook: {}", e);
312 }
313 }
314 }
315}
316
317impl Default for LifecycleHookRegistry {
318 fn default() -> Self {
319 Self::new()
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326
327 struct TestHook {
328 before_request_called: Arc<RwLock<bool>>,
329 after_response_called: Arc<RwLock<bool>>,
330 }
331
332 #[async_trait]
333 impl LifecycleHook for TestHook {
334 async fn before_request(
335 &self,
336 _ctx: &RequestContext,
337 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
338 *self.before_request_called.write().await = true;
339 Ok(())
340 }
341
342 async fn after_response(
343 &self,
344 _ctx: &ResponseContext,
345 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
346 *self.after_response_called.write().await = true;
347 Ok(())
348 }
349 }
350
351 #[tokio::test]
352 async fn test_lifecycle_hooks() {
353 let registry = LifecycleHookRegistry::new();
354 let before_called = Arc::new(RwLock::new(false));
355 let after_called = Arc::new(RwLock::new(false));
356
357 let hook = Arc::new(TestHook {
358 before_request_called: before_called.clone(),
359 after_response_called: after_called.clone(),
360 });
361
362 registry.register_hook(hook).await;
363
364 let request_ctx = RequestContext {
365 method: "GET".to_string(),
366 path: "/test".to_string(),
367 headers: HashMap::new(),
368 query_params: HashMap::new(),
369 body: None,
370 request_id: "test-1".to_string(),
371 timestamp: chrono::Utc::now(),
372 };
373
374 registry.invoke_before_request(&request_ctx).await;
375 assert!(*before_called.read().await);
376
377 let response_ctx = ResponseContext {
378 request: request_ctx,
379 status_code: 200,
380 headers: HashMap::new(),
381 body: None,
382 response_time_ms: 10,
383 timestamp: chrono::Utc::now(),
384 };
385
386 registry.invoke_after_response(&response_ctx).await;
387 assert!(*after_called.read().await);
388 }
389}