pharos_runtime_core/
request.rs1use crate::builtin::{FrameworkBuiltinResponse, framework_builtin_response};
2use crate::config::AppRuntime;
3use crate::stack::RuntimeStack;
4
5pub trait RuntimeRequestService {
7 fn route_match_lookup(&self, target: &str) -> Option<String>;
9
10 fn route_match_store(&self, target: &str, app_id: &str);
12
13 fn framework_webhook_endpoint_id(&self, target: &str) -> Option<String>;
15
16 fn app_declares_framework_webhook_endpoint(&self, app: &AppRuntime, endpoint_id: &str) -> bool;
18}
19
20pub fn runtime_match_app<'a>(
22 stack: &'a RuntimeStack,
23 target: &str,
24 service: &impl RuntimeRequestService,
25) -> Option<&'a AppRuntime> {
26 if let Some(app_id) = service.route_match_lookup(target)
27 && let Some(app) = stack
28 .apps
29 .iter()
30 .find(|app| app.app_id.as_deref() == Some(app_id.as_str()))
31 {
32 return Some(app);
33 }
34
35 if let Some(endpoint_id) = service.framework_webhook_endpoint_id(target) {
36 for app in &stack.apps {
37 if service.app_declares_framework_webhook_endpoint(app, &endpoint_id) {
38 if let Some(app_id) = app.app_id.as_deref() {
39 service.route_match_store(target, app_id);
40 }
41 return Some(app);
42 }
43 }
44 }
45
46 crate::best_mount_index(
47 target,
48 stack
49 .apps
50 .iter()
51 .enumerate()
52 .map(|(index, app)| (index, app.base_path.as_deref().unwrap_or_default())),
53 )
54 .and_then(|index| stack.apps.get(index))
55 .inspect(|app| {
56 if let Some(app_id) = app.app_id.as_deref() {
57 service.route_match_store(target, app_id);
58 }
59 })
60}
61
62#[allow(clippy::too_many_arguments)]
64pub fn dynamic_runtime_handle_request(
65 method: &str,
66 target: &str,
67 stack: &RuntimeStack,
68 version: &str,
69 target_id: &str,
70 app_ready: impl Fn(&AppRuntime) -> bool,
71 stack_ready: impl Fn(&RuntimeStack) -> bool,
72 service: &impl RuntimeRequestService,
73) -> Option<FrameworkBuiltinResponse> {
74 let app = runtime_match_app(stack, target, service);
75 framework_builtin_response(
76 method,
77 target,
78 app,
79 Some(stack),
80 version,
81 target_id,
82 app_ready,
83 stack_ready,
84 )
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[derive(Default)]
92 struct TestService;
93
94 impl RuntimeRequestService for TestService {
95 fn route_match_lookup(&self, _target: &str) -> Option<String> {
96 None
97 }
98
99 fn route_match_store(&self, _target: &str, _app_id: &str) {}
100
101 fn framework_webhook_endpoint_id(&self, _target: &str) -> Option<String> {
102 None
103 }
104
105 fn app_declares_framework_webhook_endpoint(
106 &self,
107 _app: &AppRuntime,
108 _endpoint_id: &str,
109 ) -> bool {
110 false
111 }
112 }
113
114 #[test]
115 fn dynamic_request_resolves_builtins_through_core_dispatch() {
116 let stack = RuntimeStack {
117 apps: vec![AppRuntime {
118 app_id: Some("docs".to_owned()),
119 base_path: Some("/docs".to_owned()),
120 artifact_dir: Some("dist/apps/docs".to_owned()),
121 ..AppRuntime::default()
122 }],
123 ..RuntimeStack::default()
124 };
125 let response = dynamic_runtime_handle_request(
126 "GET",
127 "/health",
128 &stack,
129 "0.0.0",
130 "test-target",
131 |_| true,
132 |_| true,
133 &TestService,
134 )
135 .expect("builtin response");
136 assert_eq!(response.status_code, 200);
137 }
138}