mockforge_core/workspace/
request.rs

1//! Mock request handling and processing
2//!
3//! This module provides functionality for processing mock requests,
4//! including request matching, response generation, and request execution.
5
6use crate::cache::{Cache, CachedResponse, ResponseCache};
7use crate::performance::PerformanceMonitor;
8use crate::templating::TemplateEngine;
9use crate::workspace::core::{EntityId, Folder, MockRequest, MockResponse, Workspace};
10use crate::{
11    routing::{HttpMethod, Route, RouteRegistry},
12    Error, Result,
13};
14use chrono::{DateTime, Utc};
15use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17use std::sync::Arc;
18use std::time::Duration;
19
20/// Request execution result
21#[derive(Debug, Clone)]
22pub struct RequestExecutionResult {
23    /// Request ID that was executed
24    pub request_id: EntityId,
25    /// Response that was returned
26    pub response: Option<MockResponse>,
27    /// Execution duration in milliseconds
28    pub duration_ms: u64,
29    /// Whether execution was successful
30    pub success: bool,
31    /// Error message if execution failed
32    pub error: Option<String>,
33}
34
35/// Request matching criteria
36#[derive(Debug, Clone)]
37pub struct RequestMatchCriteria {
38    /// HTTP method
39    pub method: HttpMethod,
40    /// Request path/URL
41    pub path: String,
42    /// Query parameters
43    pub query_params: HashMap<String, String>,
44    /// Headers
45    pub headers: HashMap<String, String>,
46    /// Body content (optional)
47    pub body: Option<String>,
48}
49
50/// Request processor for handling mock request execution
51#[derive(Debug, Clone)]
52pub struct RequestProcessor {
53    /// Template engine for variable substitution
54    _template_engine: TemplateEngine,
55    /// Environment manager for variable resolution
56    environment_manager: Option<crate::workspace::environment::EnvironmentManager>,
57    /// Performance monitoring
58    performance_monitor: Arc<PerformanceMonitor>,
59    /// Response cache for frequently accessed responses
60    response_cache: Arc<ResponseCache>,
61    /// Request validation cache
62    validation_cache: Arc<Cache<String, RequestValidationResult>>,
63    /// Enable performance optimizations
64    optimizations_enabled: bool,
65}
66
67/// Request validation result
68#[derive(Debug, Clone)]
69pub struct RequestValidationResult {
70    /// Whether the request is valid
71    pub is_valid: bool,
72    /// Validation errors
73    pub errors: Vec<String>,
74    /// Validation warnings
75    pub warnings: Vec<String>,
76}
77
78/// Request execution context
79#[derive(Debug, Clone)]
80pub struct RequestExecutionContext {
81    /// Workspace ID
82    pub workspace_id: EntityId,
83    /// Environment variables
84    pub environment_variables: HashMap<String, String>,
85    /// Global headers
86    pub global_headers: HashMap<String, String>,
87    /// Request timeout in seconds
88    pub timeout_seconds: u64,
89    /// Whether SSL verification is enabled
90    pub ssl_verify: bool,
91}
92
93/// Request metrics
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct RequestMetrics {
96    /// Total requests executed
97    pub total_requests: u64,
98    /// Successful requests
99    pub successful_requests: u64,
100    /// Failed requests
101    pub failed_requests: u64,
102    /// Average response time in milliseconds
103    pub average_response_time_ms: f64,
104    /// Most popular requests
105    pub popular_requests: Vec<(EntityId, u64)>,
106    /// Last execution timestamp
107    pub last_execution: Option<DateTime<Utc>>,
108}
109
110impl RequestProcessor {
111    /// Create a new request processor
112    pub fn new() -> Self {
113        Self {
114            _template_engine: TemplateEngine::new(),
115            environment_manager: None,
116            performance_monitor: Arc::new(PerformanceMonitor::new()),
117            response_cache: Arc::new(ResponseCache::new(1000, Duration::from_secs(300))), // 5 min TTL
118            validation_cache: Arc::new(Cache::with_ttl(500, Duration::from_secs(60))), // 1 min TTL
119            optimizations_enabled: true,
120        }
121    }
122
123    /// Create a new request processor with environment manager
124    pub fn with_environment_manager(
125        environment_manager: crate::workspace::environment::EnvironmentManager,
126    ) -> Self {
127        Self {
128            _template_engine: TemplateEngine::new(),
129            environment_manager: Some(environment_manager),
130            performance_monitor: Arc::new(PerformanceMonitor::new()),
131            response_cache: Arc::new(ResponseCache::new(1000, Duration::from_secs(300))),
132            validation_cache: Arc::new(Cache::with_ttl(500, Duration::from_secs(60))),
133            optimizations_enabled: true,
134        }
135    }
136
137    /// Create a request processor with custom performance settings
138    pub fn with_performance_config(
139        environment_manager: Option<crate::workspace::environment::EnvironmentManager>,
140        cache_size: usize,
141        cache_ttl: Duration,
142        enable_optimizations: bool,
143    ) -> Self {
144        Self {
145            _template_engine: TemplateEngine::new(),
146            environment_manager,
147            performance_monitor: Arc::new(PerformanceMonitor::new()),
148            response_cache: Arc::new(ResponseCache::new(cache_size, cache_ttl)),
149            validation_cache: Arc::new(Cache::with_ttl(cache_size / 2, Duration::from_secs(60))),
150            optimizations_enabled: enable_optimizations,
151        }
152    }
153
154    /// Get performance monitor
155    pub fn performance_monitor(&self) -> Arc<PerformanceMonitor> {
156        self.performance_monitor.clone()
157    }
158
159    /// Enable or disable performance optimizations
160    pub fn set_optimizations_enabled(&mut self, enabled: bool) {
161        self.optimizations_enabled = enabled;
162    }
163
164    /// Find a request that matches the given criteria
165    pub fn find_matching_request(
166        &self,
167        workspace: &Workspace,
168        criteria: &RequestMatchCriteria,
169    ) -> Option<EntityId> {
170        // Search root requests
171        for request in &workspace.requests {
172            if self.request_matches(request, criteria) {
173                return Some(request.id.clone());
174            }
175        }
176
177        // Search folder requests
178        if let Some(request_id) =
179            self.find_matching_request_in_folders(&workspace.folders, criteria)
180        {
181            return Some(request_id);
182        }
183
184        None
185    }
186
187    /// Check if a request matches the given criteria
188    fn request_matches(&self, request: &MockRequest, criteria: &RequestMatchCriteria) -> bool {
189        // Check HTTP method
190        if request.method != criteria.method {
191            return false;
192        }
193
194        // Check URL pattern matching
195        if !self.url_matches_pattern(&request.url, &criteria.path) {
196            return false;
197        }
198
199        // Check query parameters
200        for (key, expected_value) in &criteria.query_params {
201            if let Some(actual_value) = request.query_params.get(key) {
202                if actual_value != expected_value {
203                    return false;
204                }
205            } else {
206                return false;
207            }
208        }
209
210        // Check headers (basic implementation)
211        for (key, expected_value) in &criteria.headers {
212            if let Some(actual_value) = request.headers.get(key) {
213                if actual_value != expected_value {
214                    return false;
215                }
216            } else {
217                return false;
218            }
219        }
220
221        true
222    }
223
224    /// Check if URL matches pattern
225    pub fn url_matches_pattern(&self, pattern: &str, url: &str) -> bool {
226        // Exact match
227        if pattern == url {
228            return true;
229        }
230
231        // Handle special case for root wildcard
232        if pattern == "*" {
233            return true;
234        }
235
236        // Handle wildcard patterns
237        if pattern.contains('*') {
238            return self.matches_path_pattern(pattern, url);
239        }
240
241        false
242    }
243
244    /// Check if a URL path matches a pattern with wildcards
245    fn matches_path_pattern(&self, pattern: &str, path: &str) -> bool {
246        let pattern_parts: Vec<&str> = pattern.split('/').filter(|s| !s.is_empty()).collect();
247        let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
248
249        self.match_segments(&pattern_parts, &path_parts, 0, 0)
250    }
251
252    /// Recursive function to match path segments with wildcards
253    #[allow(clippy::only_used_in_recursion)]
254    fn match_segments(
255        &self,
256        pattern_parts: &[&str],
257        path_parts: &[&str],
258        pattern_idx: usize,
259        path_idx: usize,
260    ) -> bool {
261        // If we've consumed both patterns and paths, it's a match
262        if pattern_idx == pattern_parts.len() && path_idx == path_parts.len() {
263            return true;
264        }
265
266        // If we've consumed the pattern but not the path, no match
267        if pattern_idx == pattern_parts.len() {
268            return false;
269        }
270
271        let current_pattern = pattern_parts[pattern_idx];
272
273        match current_pattern {
274            "*" => {
275                // Single wildcard: try matching with current path segment
276                if path_idx < path_parts.len() {
277                    // Try consuming one segment
278                    if self.match_segments(pattern_parts, path_parts, pattern_idx + 1, path_idx + 1)
279                    {
280                        return true;
281                    }
282                }
283                false
284            }
285            "**" => {
286                // Double wildcard: can match zero or more segments
287                // Try matching zero segments (skip this pattern)
288                if self.match_segments(pattern_parts, path_parts, pattern_idx + 1, path_idx) {
289                    return true;
290                }
291                // Try matching one or more segments
292                if path_idx < path_parts.len()
293                    && self.match_segments(pattern_parts, path_parts, pattern_idx, path_idx + 1)
294                {
295                    return true;
296                }
297                false
298            }
299            _ => {
300                // Exact match required
301                if path_idx < path_parts.len() && current_pattern == path_parts[path_idx] {
302                    return self.match_segments(
303                        pattern_parts,
304                        path_parts,
305                        pattern_idx + 1,
306                        path_idx + 1,
307                    );
308                }
309                false
310            }
311        }
312    }
313
314    /// Find matching request in folder hierarchy
315    fn find_matching_request_in_folders(
316        &self,
317        folders: &[Folder],
318        criteria: &RequestMatchCriteria,
319    ) -> Option<EntityId> {
320        for folder in folders {
321            // Search folder requests
322            for request in &folder.requests {
323                if self.request_matches(request, criteria) {
324                    return Some(request.id.clone());
325                }
326            }
327
328            // Search subfolders
329            if let Some(request_id) =
330                self.find_matching_request_in_folders(&folder.folders, criteria)
331            {
332                return Some(request_id);
333            }
334        }
335
336        None
337    }
338
339    /// Execute a mock request
340    pub async fn execute_request(
341        &self,
342        workspace: &mut Workspace,
343        request_id: &EntityId,
344        context: &RequestExecutionContext,
345    ) -> Result<RequestExecutionResult> {
346        // Start performance tracking
347        let _perf_guard = if self.optimizations_enabled {
348            self.performance_monitor.start_tracking_named("execute_request")
349        } else {
350            None
351        };
352
353        // Generate cache key for response caching if optimizations are enabled
354        let cache_key = if self.optimizations_enabled {
355            self.generate_response_cache_key(request_id, context)
356        } else {
357            String::new()
358        };
359
360        // Check response cache first
361        if self.optimizations_enabled && !cache_key.is_empty() {
362            if let Some(cached_response) = self.response_cache.get_response(&cache_key).await {
363                self.performance_monitor.record_cache_hit();
364                return Ok(RequestExecutionResult {
365                    request_id: request_id.clone(),
366                    response: Some(self.convert_cached_response_to_mock_response(cached_response)),
367                    duration_ms: 1, // Cached responses are nearly instant
368                    success: true,
369                    error: None,
370                });
371            } else {
372                self.performance_monitor.record_cache_miss();
373            }
374        }
375
376        // Find the request
377        let request = self.find_request_in_workspace(workspace, request_id).ok_or_else(|| {
378            if self.optimizations_enabled {
379                self.performance_monitor.record_error();
380            }
381            format!("Request with ID {} not found", request_id)
382        })?;
383
384        let start_time = std::time::Instant::now();
385
386        // Validate request with caching
387        let validation = self.validate_request_cached(request, context).await?;
388        if !validation.is_valid {
389            if self.optimizations_enabled {
390                self.performance_monitor.record_error();
391            }
392            return Err(Error::Validation {
393                message: format!("Request validation failed: {:?}", validation.errors),
394            });
395        }
396
397        // Get active response
398        let response = request.active_response().ok_or_else(|| {
399            if self.optimizations_enabled {
400                self.performance_monitor.record_error();
401            }
402            Error::generic("No active response found for request")
403        })?;
404
405        // Apply variable substitution
406        let processed_response = self.process_response(response, context).await?;
407
408        let duration_ms = start_time.elapsed().as_millis() as u64;
409
410        // Cache the response if optimizations are enabled
411        if self.optimizations_enabled && !cache_key.is_empty() {
412            let cached_response =
413                self.convert_mock_response_to_cached_response(&processed_response);
414            self.response_cache.cache_response(cache_key, cached_response).await;
415        }
416
417        // Record response usage
418        if let Some(request_mut) = self.find_request_in_workspace_mut(workspace, request_id) {
419            if let Some(response_mut) = request_mut.active_response_mut() {
420                response_mut.record_usage(request_id.clone(), duration_ms);
421            }
422        }
423
424        Ok(RequestExecutionResult {
425            request_id: request_id.clone(),
426            response: Some(processed_response),
427            duration_ms,
428            success: true,
429            error: None,
430        })
431    }
432
433    /// Find request in workspace (mutable)
434    fn find_request_in_workspace_mut<'a>(
435        &self,
436        workspace: &'a mut Workspace,
437        request_id: &EntityId,
438    ) -> Option<&'a mut MockRequest> {
439        // Search root requests
440        for request in &mut workspace.requests {
441            if &request.id == request_id {
442                return Some(request);
443            }
444        }
445
446        // Search folder requests
447        self.find_request_in_folders_mut(&mut workspace.folders, request_id)
448    }
449
450    /// Find request in folder hierarchy (mutable)
451    #[allow(clippy::only_used_in_recursion)]
452    fn find_request_in_folders_mut<'a>(
453        &self,
454        folders: &'a mut [Folder],
455        request_id: &EntityId,
456    ) -> Option<&'a mut MockRequest> {
457        for folder in folders {
458            // Search folder requests
459            for request in &mut folder.requests {
460                if &request.id == request_id {
461                    return Some(request);
462                }
463            }
464
465            // Search subfolders
466            if let Some(request) = self.find_request_in_folders_mut(&mut folder.folders, request_id)
467            {
468                return Some(request);
469            }
470        }
471
472        None
473    }
474
475    /// Find request in workspace (immutable)
476    fn find_request_in_workspace<'a>(
477        &self,
478        workspace: &'a Workspace,
479        request_id: &EntityId,
480    ) -> Option<&'a MockRequest> {
481        // Search root requests
482        workspace
483            .requests
484            .iter()
485            .find(|r| &r.id == request_id)
486            .or_else(|| self.find_request_in_folders(&workspace.folders, request_id))
487    }
488
489    /// Find request in folder hierarchy (immutable)
490    #[allow(clippy::only_used_in_recursion)]
491    fn find_request_in_folders<'a>(
492        &self,
493        folders: &'a [Folder],
494        request_id: &EntityId,
495    ) -> Option<&'a MockRequest> {
496        for folder in folders {
497            // Search folder requests
498            if let Some(request) = folder.requests.iter().find(|r| &r.id == request_id) {
499                return Some(request);
500            }
501
502            // Search subfolders
503            if let Some(request) = self.find_request_in_folders(&folder.folders, request_id) {
504                return Some(request);
505            }
506        }
507
508        None
509    }
510
511    /// Validate a request
512    pub fn validate_request(
513        &self,
514        request: &MockRequest,
515        _context: &RequestExecutionContext,
516    ) -> RequestValidationResult {
517        let mut errors = Vec::new();
518        let mut warnings = Vec::new();
519
520        // Check if request is enabled
521        if !request.enabled {
522            errors.push("Request is disabled".to_string());
523        }
524
525        // Validate URL
526        if request.url.is_empty() {
527            errors.push("Request URL cannot be empty".to_string());
528        }
529
530        // Validate method
531        match request.method {
532            HttpMethod::GET
533            | HttpMethod::POST
534            | HttpMethod::PUT
535            | HttpMethod::DELETE
536            | HttpMethod::PATCH
537            | HttpMethod::HEAD
538            | HttpMethod::OPTIONS => {
539                // Valid methods
540            }
541        }
542
543        // Check for active response
544        if request.active_response().is_none() {
545            warnings.push("No active response configured".to_string());
546        }
547
548        // Validate responses
549        for response in &request.responses {
550            if response.status_code < 100 || response.status_code > 599 {
551                errors.push(format!("Invalid status code: {}", response.status_code));
552            }
553
554            if response.body.is_empty() {
555                warnings.push(format!("Response '{}' has empty body", response.name));
556            }
557        }
558
559        RequestValidationResult {
560            is_valid: errors.is_empty(),
561            errors,
562            warnings,
563        }
564    }
565
566    /// Process response with variable substitution and delays
567    async fn process_response(
568        &self,
569        response: &MockResponse,
570        context: &RequestExecutionContext,
571    ) -> Result<MockResponse> {
572        // Apply delay if configured
573        if response.delay > 0 {
574            tokio::time::sleep(std::time::Duration::from_millis(response.delay)).await;
575        }
576
577        // Create processed response
578        let mut processed_response = response.clone();
579
580        // Apply environment variable substitution
581        if let Some(env_manager) = &self.environment_manager {
582            if let Some(_env_vars) = self.get_environment_variables(context) {
583                processed_response.body = env_manager.substitute_variables(&response.body).value;
584            }
585        }
586
587        Ok(processed_response)
588    }
589
590    /// Get environment variables for context
591    fn get_environment_variables(
592        &self,
593        context: &RequestExecutionContext,
594    ) -> Option<HashMap<String, String>> {
595        if let Some(env_manager) = &self.environment_manager {
596            if let Some(active_env) = env_manager.get_active_environment() {
597                return Some(active_env.variables.clone());
598            }
599        }
600
601        Some(context.environment_variables.clone())
602    }
603
604    /// Get request metrics for a workspace
605    pub fn get_request_metrics(&self, workspace: &Workspace) -> RequestMetrics {
606        let mut total_requests = 0u64;
607        let mut successful_requests = 0u64;
608        let mut failed_requests = 0u64;
609        let mut total_response_time = 0u64;
610        let mut request_counts = HashMap::new();
611        let mut last_execution: Option<DateTime<Utc>> = None;
612
613        // Collect metrics from all requests
614        for request in &workspace.requests {
615            total_requests += 1;
616
617            // Count executions from response history
618            for response in &request.responses {
619                let execution_count = response.history.len() as u64;
620                *request_counts.entry(request.id.clone()).or_insert(0) += execution_count;
621
622                for entry in &response.history {
623                    total_response_time += entry.duration_ms;
624
625                    // Update last execution timestamp
626                    if let Some(current_last) = last_execution {
627                        if entry.timestamp > current_last {
628                            last_execution = Some(entry.timestamp);
629                        }
630                    } else {
631                        last_execution = Some(entry.timestamp);
632                    }
633
634                    // Simple success determination (could be improved)
635                    if entry.duration_ms < 5000 {
636                        // Less than 5 seconds
637                        successful_requests += 1;
638                    } else {
639                        failed_requests += 1;
640                    }
641                }
642            }
643        }
644
645        // Also collect from folder requests
646        self.collect_folder_request_metrics(
647            &workspace.folders,
648            &mut total_requests,
649            &mut successful_requests,
650            &mut failed_requests,
651            &mut total_response_time,
652            &mut request_counts,
653            &mut last_execution,
654        );
655
656        let average_response_time = if total_requests > 0 {
657            total_response_time as f64 / total_requests as f64
658        } else {
659            0.0
660        };
661
662        // Get popular requests (top 5)
663        let mut popular_requests: Vec<_> = request_counts.into_iter().collect();
664        popular_requests.sort_by(|a, b| b.1.cmp(&a.1));
665        popular_requests.truncate(5);
666
667        RequestMetrics {
668            total_requests,
669            successful_requests,
670            failed_requests,
671            average_response_time_ms: average_response_time,
672            popular_requests,
673            last_execution,
674        }
675    }
676
677    /// Collect metrics from folder requests
678    #[allow(clippy::only_used_in_recursion)]
679    #[allow(clippy::too_many_arguments)]
680    fn collect_folder_request_metrics(
681        &self,
682        folders: &[Folder],
683        total_requests: &mut u64,
684        successful_requests: &mut u64,
685        failed_requests: &mut u64,
686        total_response_time: &mut u64,
687        request_counts: &mut HashMap<EntityId, u64>,
688        last_execution: &mut Option<DateTime<Utc>>,
689    ) {
690        for folder in folders {
691            for request in &folder.requests {
692                *total_requests += 1;
693
694                // Count executions from response history
695                for response in &request.responses {
696                    let execution_count = response.history.len() as u64;
697                    *request_counts.entry(request.id.clone()).or_insert(0) += execution_count;
698
699                    for entry in &response.history {
700                        *total_response_time += entry.duration_ms;
701
702                        // Update last execution timestamp
703                        if let Some(current_last) = *last_execution {
704                            if entry.timestamp > current_last {
705                                *last_execution = Some(entry.timestamp);
706                            }
707                        } else {
708                            *last_execution = Some(entry.timestamp);
709                        }
710
711                        // Simple success determination
712                        if entry.duration_ms < 5000 {
713                            *successful_requests += 1;
714                        } else {
715                            *failed_requests += 1;
716                        }
717                    }
718                }
719            }
720
721            // Recurse into subfolders
722            self.collect_folder_request_metrics(
723                &folder.folders,
724                total_requests,
725                successful_requests,
726                failed_requests,
727                total_response_time,
728                request_counts,
729                last_execution,
730            );
731        }
732    }
733
734    /// Create a route from a mock request
735    pub fn create_route_from_request(&self, request: &MockRequest) -> Result<Route> {
736        if !request.enabled {
737            return Err(Error::validation("Request is disabled"));
738        }
739
740        let response = request
741            .active_response()
742            .ok_or_else(|| Error::validation("No active response found"))?;
743
744        // Create route with request information
745        let mut route = Route::new(request.method.clone(), request.url.clone());
746
747        // Store additional data in metadata
748        route.metadata.insert("id".to_string(), serde_json::json!(request.id));
749        route.metadata.insert("response".to_string(), serde_json::json!(response.body));
750        route
751            .metadata
752            .insert("status_code".to_string(), serde_json::json!(response.status_code));
753        route.metadata.insert("headers".to_string(), serde_json::json!(request.headers));
754        route
755            .metadata
756            .insert("query_params".to_string(), serde_json::json!(request.query_params));
757        route.metadata.insert("enabled".to_string(), serde_json::json!(request.enabled));
758        route
759            .metadata
760            .insert("created_at".to_string(), serde_json::json!(request.created_at));
761        route
762            .metadata
763            .insert("updated_at".to_string(), serde_json::json!(request.updated_at));
764
765        Ok(route)
766    }
767
768    /// Update route registry with workspace requests
769    pub fn update_route_registry(
770        &self,
771        workspace: &Workspace,
772        route_registry: &mut RouteRegistry,
773    ) -> Result<()> {
774        route_registry.clear();
775
776        // Add root requests
777        for request in &workspace.requests {
778            if request.enabled {
779                if let Ok(route) = self.create_route_from_request(request) {
780                    let _ = route_registry.add_route(route);
781                }
782            }
783        }
784
785        // Add folder requests
786        self.add_folder_routes_to_registry(&workspace.folders, route_registry)?;
787
788        Ok(())
789    }
790
791    /// Add folder requests to route registry
792    fn add_folder_routes_to_registry(
793        &self,
794        folders: &[Folder],
795        route_registry: &mut RouteRegistry,
796    ) -> Result<()> {
797        for folder in folders {
798            for request in &folder.requests {
799                if request.enabled {
800                    if let Ok(route) = self.create_route_from_request(request) {
801                        let _ = route_registry.add_route(route);
802                    }
803                }
804            }
805
806            // Recurse into subfolders
807            self.add_folder_routes_to_registry(&folder.folders, route_registry)?;
808        }
809
810        Ok(())
811    }
812
813    // Performance optimization helper methods
814
815    /// Generate cache key for response caching
816    fn generate_response_cache_key(
817        &self,
818        request_id: &EntityId,
819        context: &RequestExecutionContext,
820    ) -> String {
821        use std::collections::hash_map::DefaultHasher;
822        use std::hash::{Hash, Hasher};
823
824        let mut hasher = DefaultHasher::new();
825        request_id.hash(&mut hasher);
826        context.workspace_id.hash(&mut hasher);
827
828        // Hash environment variables
829        for (key, value) in &context.environment_variables {
830            key.hash(&mut hasher);
831            value.hash(&mut hasher);
832        }
833
834        // Hash global headers
835        for (key, value) in &context.global_headers {
836            key.hash(&mut hasher);
837            value.hash(&mut hasher);
838        }
839
840        format!("req_{}_{}", hasher.finish(), request_id)
841    }
842
843    /// Validate request with caching
844    async fn validate_request_cached(
845        &self,
846        request: &MockRequest,
847        context: &RequestExecutionContext,
848    ) -> Result<RequestValidationResult> {
849        if !self.optimizations_enabled {
850            return Ok(self.validate_request(request, context));
851        }
852
853        // Generate validation cache key
854        let cache_key = format!("val_{}_{}", request.id, context.workspace_id);
855
856        // Check cache first
857        if let Some(cached_result) = self.validation_cache.get(&cache_key).await {
858            return Ok(cached_result);
859        }
860
861        // Perform validation
862        let result = self.validate_request(request, context);
863
864        // Cache the result
865        self.validation_cache.insert(cache_key, result.clone(), None).await;
866
867        Ok(result)
868    }
869
870    /// Convert MockResponse to CachedResponse
871    fn convert_mock_response_to_cached_response(&self, response: &MockResponse) -> CachedResponse {
872        CachedResponse {
873            status_code: response.status_code,
874            headers: response.headers.clone(),
875            body: response.body.clone(),
876            content_type: response.headers.get("Content-Type").cloned(),
877        }
878    }
879
880    /// Convert CachedResponse to MockResponse
881    fn convert_cached_response_to_mock_response(&self, cached: CachedResponse) -> MockResponse {
882        MockResponse {
883            id: EntityId::new(),
884            name: "Cached Response".to_string(),
885            status_code: cached.status_code,
886            headers: cached.headers,
887            body: cached.body,
888            delay: 0, // Cached responses have no additional delay
889            active: true,
890            created_at: chrono::Utc::now(),
891            updated_at: chrono::Utc::now(),
892            history: Vec::new(),
893            intelligent: None,
894            drift: None,
895        }
896    }
897
898    /// Get performance summary
899    pub async fn get_performance_summary(&self) -> crate::performance::PerformanceSummary {
900        self.performance_monitor.get_summary().await
901    }
902
903    /// Get cache statistics
904    pub async fn get_cache_stats(&self) -> (crate::cache::CacheStats, crate::cache::CacheStats) {
905        let response_cache_stats = self.response_cache.stats().await;
906        let validation_cache_stats = self.validation_cache.stats().await;
907        (response_cache_stats, validation_cache_stats)
908    }
909
910    /// Clear all caches
911    pub async fn clear_caches(&self) {
912        self.response_cache.get_response("").await; // Dummy call to access underlying cache
913        self.validation_cache.clear().await;
914    }
915}
916
917impl Default for RequestProcessor {
918    fn default() -> Self {
919        Self::new()
920    }
921}