app_facade_api/
routing.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ExecutionSelector {
10 Cloud,
12 ScopeMode,
14 Hub,
16 RequestPlacement,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum RelayPolicy {
22 DirectOnly,
24 CloudRelayAllowed,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct RoutePolicy {
30 pub execution: ExecutionSelector,
31 pub relay: RelayPolicy,
32}
33
34const DIRECT_CLOUD: RoutePolicy = RoutePolicy {
35 execution: ExecutionSelector::Cloud,
36 relay: RelayPolicy::DirectOnly,
37};
38const DIRECT_SCOPE: RoutePolicy = RoutePolicy {
39 execution: ExecutionSelector::ScopeMode,
40 relay: RelayPolicy::DirectOnly,
41};
42const DIRECT_HUB: RoutePolicy = RoutePolicy {
43 execution: ExecutionSelector::Hub,
44 relay: RelayPolicy::DirectOnly,
45};
46const RELAYABLE_HUB: RoutePolicy = RoutePolicy {
47 execution: ExecutionSelector::Hub,
48 relay: RelayPolicy::CloudRelayAllowed,
49};
50const REQUEST_PLACEMENT: RoutePolicy = RoutePolicy {
51 execution: ExecutionSelector::RequestPlacement,
52 relay: RelayPolicy::DirectOnly,
53};
54
55include!("routing.generated.rs");
56
57pub fn route_policy_for_target(target: &str) -> Option<RoutePolicy> {
63 if !target.starts_with(TARGET_PREFIX) {
64 return None;
65 }
66
67 if SCOPE_MODE_TARGETS.contains(&target) {
69 return Some(DIRECT_SCOPE);
70 }
71 if CLOUD_TARGETS.contains(&target) {
72 return Some(DIRECT_CLOUD);
73 }
74 if CLOUD_RELAY_TARGETS.contains(&target) {
75 return Some(RELAYABLE_HUB);
76 }
77
78 if REQUEST_PLACEMENT_PREFIXES
79 .iter()
80 .any(|prefix| target.starts_with(prefix))
81 {
82 return Some(REQUEST_PLACEMENT);
83 }
84 if HUB_PREFIXES.iter().any(|prefix| target.starts_with(prefix)) {
85 return Some(DIRECT_HUB);
86 }
87 if CLOUD_PREFIXES
88 .iter()
89 .any(|prefix| target.starts_with(prefix))
90 {
91 return Some(DIRECT_CLOUD);
92 }
93
94 Some(DIRECT_SCOPE)
95}
96
97pub fn request_placement(payload: &str) -> Option<crate::messaging::Placement> {
100 let call: crate::FacadeCall = serde_json::from_str(payload).ok()?;
101 serde_json::from_value(call.input.get("placement")?.clone()).ok()
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn exact_exceptions_win_over_domain_defaults() {
110 assert_eq!(
111 route_policy_for_target("/app/scope/context/get"),
112 Some(DIRECT_SCOPE)
113 );
114 assert_eq!(
115 route_policy_for_target(crate::plugin::ENABLED_LIST_TARGET),
116 Some(RELAYABLE_HUB)
117 );
118 assert_eq!(
119 route_policy_for_target(crate::system::INSTANCES_TARGET),
120 Some(RELAYABLE_HUB)
121 );
122 assert_eq!(
123 route_policy_for_target(crate::plugin::ENABLE_START_TARGET),
124 Some(DIRECT_HUB)
125 );
126 }
127
128 #[test]
129 fn domains_select_their_authoritative_executor() {
130 assert_eq!(
131 route_policy_for_target("/app/scope/member/list"),
132 Some(DIRECT_CLOUD)
133 );
134 assert_eq!(
135 route_policy_for_target("/app/messaging/provider/list"),
136 Some(REQUEST_PLACEMENT)
137 );
138 assert_eq!(
139 route_policy_for_target("/app/object-storage/session/acquire"),
140 Some(DIRECT_HUB)
141 );
142 assert_eq!(
143 route_policy_for_target("/app/runtime/status/list"),
144 Some(DIRECT_HUB)
145 );
146 assert_eq!(
147 route_policy_for_target(crate::system::INSTANCES_TARGET),
148 Some(RELAYABLE_HUB)
149 );
150 assert_eq!(
151 route_policy_for_target(crate::system::HOSTS_TARGET),
152 Some(RELAYABLE_HUB)
153 );
154 assert_eq!(
155 route_policy_for_target(crate::system::HOSTS_RUNTIME_TARGET),
156 Some(RELAYABLE_HUB)
157 );
158 assert_eq!(
159 route_policy_for_target(crate::system::HOSTS_CLAIM_TARGET),
160 Some(RELAYABLE_HUB)
161 );
162 assert_eq!(
163 route_policy_for_target("/app/device/list"),
164 Some(DIRECT_SCOPE)
165 );
166 }
167
168 #[test]
169 fn legacy_targets_are_outside_the_contract() {
170 assert_eq!(route_policy_for_target("/scope/get"), None);
171 assert_eq!(route_policy_for_target("/chat"), None);
172 }
173
174 #[test]
175 fn placement_is_read_only_from_the_canonical_input() {
176 assert_eq!(
177 request_placement(r#"{"control":{"mode":"direct"},"input":{"placement":"local"}}"#),
178 Some(crate::messaging::Placement::Local)
179 );
180 assert_eq!(request_placement(r#"{"placement":"cloud"}"#), None);
181 assert_eq!(
182 request_placement(r#"{"control":{"mode":"direct"},"input":{}}"#),
183 None
184 );
185 }
186}