omni_director/routing/
router.rs

1//! # Request Router
2//!
3//! Central router that handles all incoming requests and routes them to the appropriate providers.
4
5use super::{Route, resolver::RouteResolver};
6use crate::providers::{ProviderRegistry, ProviderError, ProviderResult};
7use std::collections::HashMap;
8use std::sync::Arc;
9use serde_json::Value;
10
11/// Router handles all incoming requests and routes them to providers
12pub struct Router {
13    /// Provider registry
14    registry: Arc<ProviderRegistry>,
15    /// Route resolver for finding providers
16    resolver: RouteResolver,
17}
18
19impl Router {
20    /// Create a new router
21    pub fn new(registry: Arc<ProviderRegistry>) -> Self {
22        Self {
23            resolver: RouteResolver::new(Arc::clone(&registry)),
24            registry,
25        }
26    }
27
28    /// Route a request to the appropriate provider
29    pub async fn route_request(
30        &self,
31        route: Route,
32        args: HashMap<String, Value>,
33    ) -> ProviderResult<Value> {
34        // Resolve the route to ensure provider/feature/operation exists
35        self.resolver.resolve_route(&route).await?;
36
37        // Execute the operation through the registry
38        self.registry
39            .execute_operation(&route.provider, &route.feature, &route.operation, args)
40            .await
41    }
42
43    /// Route a request from a path string
44    pub async fn route_path(
45        &self,
46        path: &str,
47        args: HashMap<String, Value>,
48    ) -> ProviderResult<Value> {
49        let route = Route::from_path(path)
50            .map_err(|e| ProviderError::InvalidRoute(e))?;
51        
52        self.route_request(route, args).await
53    }
54
55    /// Route a request from URL path (removes /providers/ prefix)
56    pub async fn route_url_path(
57        &self,
58        url_path: &str,
59        args: HashMap<String, Value>,
60    ) -> ProviderResult<Value> {
61        // Remove /providers/ prefix if present
62        let path = if url_path.starts_with("/providers/") {
63            &url_path[11..] // Remove "/providers/" (11 chars)
64        } else {
65            url_path
66        };
67
68        // Remove /features/ and /operations/ segments to get provider/feature/operation
69        let normalized_path = path
70            .replace("/features/", "/")
71            .replace("/operations/", "/");
72
73        self.route_path(&normalized_path, args).await
74    }
75
76    /// Get all available routes
77    pub async fn get_available_routes(&self) -> ProviderResult<Vec<Route>> {
78        self.resolver.list_all_routes().await
79    }
80
81    /// Get routes for a specific provider
82    pub async fn get_provider_routes(&self, provider: &str) -> ProviderResult<Vec<Route>> {
83        self.resolver.list_provider_routes(provider).await
84    }
85
86    /// Get routes for a specific feature
87    pub async fn get_feature_routes(&self, provider: &str, feature: &str) -> ProviderResult<Vec<Route>> {
88        self.resolver.list_feature_routes(provider, feature).await
89    }
90
91    /// Check if a route is valid
92    pub async fn is_route_valid(&self, route: &Route) -> bool {
93        self.resolver.resolve_route(route).await.is_ok()
94    }
95
96    /// Get route metadata
97    pub async fn get_route_metadata(&self, route: &Route) -> ProviderResult<RouteMetadata> {
98        // Validate route first
99        self.resolver.resolve_route(route).await?;
100
101        // Get provider metadata
102        let provider_metadata = self.registry
103            .get_metadata(&route.provider)
104            .await
105            .ok_or_else(|| ProviderError::NotFound(route.provider.clone()))?;
106
107        // Find the feature
108        let feature_metadata = provider_metadata
109            .features
110            .iter()
111            .find(|f| f.name == route.feature)
112            .ok_or_else(|| ProviderError::FeatureNotSupported {
113                provider: route.provider.clone(),
114                feature: route.feature.clone(),
115            })?;
116
117        // Find the operation
118        let operation_metadata = feature_metadata
119            .operations
120            .iter()
121            .find(|op| op.name == route.operation)
122            .ok_or_else(|| ProviderError::OperationNotSupported {
123                provider: route.provider.clone(),
124                feature: route.feature.clone(),
125                operation: route.operation.clone(),
126            })?;
127
128        Ok(RouteMetadata {
129            route: route.clone(),
130            provider_name: provider_metadata.name.clone(),
131            provider_version: provider_metadata.version.clone(),
132            feature_name: feature_metadata.name.clone(),
133            feature_description: feature_metadata.description.clone(),
134            operation_name: operation_metadata.name.clone(),
135            operation_description: operation_metadata.description.clone(),
136            operation_arguments: operation_metadata.arguments.clone(),
137            operation_return_type: operation_metadata.return_type.clone(),
138            is_mutating: operation_metadata.is_mutating,
139            estimated_duration_ms: operation_metadata.estimated_duration_ms,
140        })
141    }
142}
143
144/// Metadata for a specific route
145#[derive(Debug, Clone)]
146pub struct RouteMetadata {
147    pub route: Route,
148    pub provider_name: String,
149    pub provider_version: String,
150    pub feature_name: String,
151    pub feature_description: String,
152    pub operation_name: String,
153    pub operation_description: String,
154    pub operation_arguments: Vec<crate::providers::ArgumentMetadata>,
155    pub operation_return_type: String,
156    pub is_mutating: bool,
157    pub estimated_duration_ms: Option<u64>,
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use crate::providers::{ProviderContext, DefaultProviderContext};
164
165    #[tokio::test]
166    async fn test_route_parsing() {
167        let context = Arc::new(DefaultProviderContext::new());
168        let registry = Arc::new(ProviderRegistry::new(context));
169        let router = Router::new(registry);
170
171        // Test URL path routing
172        let result = router.route_url_path(
173            "/providers/virtualbox/features/vm-management/operations/start-vm",
174            HashMap::new()
175        ).await;
176
177        // Should fail because no providers are registered, but parsing should work
178        assert!(matches!(result, Err(ProviderError::NotFound(_))));
179    }
180
181    #[test]
182    fn test_url_path_normalization() {
183        let path = "/providers/virtualbox/features/vm-management/operations/start-vm";
184        let normalized = path
185            .strip_prefix("/providers/").unwrap_or(path)
186            .replace("/features/", "/")
187            .replace("/operations/", "/");
188        
189        assert_eq!(normalized, "virtualbox/vm-management/start-vm");
190    }
191}