Skip to main content

platform_provider/
binding.rs

1use crate::ProviderHostEffectCoordinator;
2use crate::config::ProviderConfig;
3use crate::event::{
4    ProviderEventHandler, ProviderEventHostActionRunner, validate_event_handler_name,
5    validate_event_name,
6};
7use crate::runtime::{ProviderRuntimeFunction, validate_function_name};
8use platform_core::{AppResult, EventHandlerRegistry};
9use platform_module::{
10    EventHandlerRegistrationContext, EventSurface, ModuleBinding, RuntimeSurface,
11};
12use platform_runtime::{FunctionDefinition, FunctionRegistry, RetryPolicy};
13use std::sync::Arc;
14use std::time::Duration;
15
16#[derive(Debug, Clone, Default)]
17pub struct ProviderBinding {
18    config: Option<ProviderConfig>,
19    functions: Vec<FunctionDefinition>,
20    event_handlers: Vec<ProviderEventHandlerRegistration>,
21    effects: Option<ProviderHostEffectCoordinator>,
22}
23
24#[derive(Debug, Clone)]
25struct ProviderEventHandlerRegistration {
26    name: String,
27    event_name: String,
28}
29
30impl ProviderBinding {
31    pub fn from_surfaces(
32        config: ProviderConfig,
33        runtime: Option<&RuntimeSurface>,
34        events: Option<&EventSurface>,
35    ) -> AppResult<Self> {
36        Self::from_surfaces_with_effects(
37            config,
38            runtime,
39            events,
40            ProviderHostEffectCoordinator::rejecting(),
41        )
42    }
43
44    pub fn from_surfaces_with_effects(
45        config: ProviderConfig,
46        runtime: Option<&RuntimeSurface>,
47        events: Option<&EventSurface>,
48        effects: ProviderHostEffectCoordinator,
49    ) -> AppResult<Self> {
50        let functions = runtime
51            .into_iter()
52            .flat_map(|surface| surface.functions.iter())
53            .map(|declaration| {
54                validate_function_name(&declaration.name)?;
55                Ok(FunctionDefinition {
56                    name: declaration.name.clone(),
57                    version: declaration.version,
58                    queue: declaration.queue.clone(),
59                    retry_policy: declaration
60                        .retry_policy
61                        .as_ref()
62                        .map(|policy| {
63                            RetryPolicy::fixed(
64                                policy.max_attempts,
65                                Duration::from_millis(policy.initial_delay_ms),
66                            )
67                        })
68                        .unwrap_or_default(),
69                    handler: Arc::new(ProviderRuntimeFunction::new(
70                        config.clone(),
71                        declaration.name.clone(),
72                        effects.clone(),
73                    )?),
74                })
75            })
76            .collect::<AppResult<Vec<_>>>()?;
77
78        let event_handlers = events
79            .into_iter()
80            .flat_map(|surface| surface.handlers.iter())
81            .map(|declaration| {
82                validate_event_handler_name(&declaration.name)?;
83                validate_event_name(&declaration.event_name)?;
84                Ok(ProviderEventHandlerRegistration {
85                    name: declaration.name.clone(),
86                    event_name: declaration.event_name.clone(),
87                })
88            })
89            .collect::<AppResult<Vec<_>>>()?;
90
91        Ok(Self {
92            config: Some(config),
93            functions,
94            event_handlers,
95            effects: Some(effects),
96        })
97    }
98}
99
100impl ModuleBinding for ProviderBinding {
101    fn register_functions(&self, registry: &mut FunctionRegistry) {
102        for function in self.functions.iter().cloned() {
103            registry.register(function);
104        }
105    }
106
107    fn register_event_handlers(
108        &self,
109        registry: &mut EventHandlerRegistry,
110        context: &EventHandlerRegistrationContext,
111    ) {
112        let (Some(config), Some(effects)) = (&self.config, &self.effects) else {
113            return;
114        };
115        let allowed_function_names = self
116            .functions
117            .iter()
118            .map(|function| function.name.clone())
119            .collect::<Vec<_>>();
120
121        for declaration in &self.event_handlers {
122            let mut handler = ProviderEventHandler::new(
123                config.clone(),
124                declaration.name.clone(),
125                declaration.event_name.clone(),
126                effects.clone(),
127            )
128            .expect("provider event handler declaration was validated");
129
130            if let Some(runtime) = context.runtime() {
131                handler = handler.with_host_action_runner(ProviderEventHostActionRunner::new(
132                    runtime.runtime_client.clone(),
133                    runtime.function_registry.clone(),
134                    allowed_function_names.clone(),
135                ));
136            }
137
138            registry.register(std::sync::Arc::new(handler));
139        }
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146    use platform_module::{
147        EventHandlerDeclaration, EventSurface, RuntimeFunctionDeclaration,
148        RuntimeRetryPolicyDeclaration, RuntimeSurface,
149    };
150
151    #[test]
152    fn provider_binding_registers_declared_functions() {
153        let binding = ProviderBinding::from_surfaces(
154            ProviderConfig::new("provider-crm", "http://127.0.0.1:4100/lenso/provider/v1"),
155            Some(&RuntimeSurface {
156                functions: vec![RuntimeFunctionDeclaration {
157                    name: "provider_crm.sync_contact.v1".to_owned(),
158                    version: 1,
159                    queue: "provider-crm".to_owned(),
160                    input_schema: Some("provider_crm.sync_contact.v1".to_owned()),
161                    retry_policy: Some(RuntimeRetryPolicyDeclaration {
162                        max_attempts: 3,
163                        initial_delay_ms: 1000,
164                    }),
165                    operation: None,
166                }],
167                schedules: vec![],
168                workflows: vec![],
169            }),
170            None,
171        )
172        .expect("provider binding should build");
173
174        let mut registry = FunctionRegistry::default();
175        binding.register_functions(&mut registry);
176
177        let definition = registry
178            .get("provider_crm.sync_contact.v1")
179            .expect("provider function should register");
180        assert_eq!(definition.version, 1);
181        assert_eq!(definition.queue, "provider-crm");
182        assert_eq!(definition.retry_policy.max_attempts, 3);
183        assert_eq!(
184            definition.retry_policy.initial_delay,
185            Duration::from_millis(1000)
186        );
187    }
188
189    #[test]
190    fn provider_binding_rejects_invalid_function_name() {
191        let error = ProviderBinding::from_surfaces(
192            ProviderConfig::new("provider-crm", "http://127.0.0.1:4100/lenso/provider/v1"),
193            Some(&RuntimeSurface {
194                functions: vec![RuntimeFunctionDeclaration {
195                    name: "provider_crm/sync_contact.v1".to_owned(),
196                    version: 1,
197                    queue: "provider-crm".to_owned(),
198                    input_schema: None,
199                    retry_policy: None,
200                    operation: None,
201                }],
202                schedules: vec![],
203                workflows: vec![],
204            }),
205            None,
206        )
207        .expect_err("invalid function name should fail");
208
209        assert_eq!(error.code, platform_core::ErrorCode::Validation);
210    }
211
212    #[test]
213    fn provider_binding_registers_declared_event_handlers() {
214        let binding = ProviderBinding::from_surfaces(
215            ProviderConfig::new("provider-crm", "http://127.0.0.1:4100/lenso/provider/v1"),
216            None,
217            Some(&EventSurface {
218                handlers: vec![EventHandlerDeclaration {
219                    name: "sync_contact_on_user_registered".to_owned(),
220                    event_name: "identity.user_registered.v1".to_owned(),
221                    operation: None,
222                }],
223            }),
224        )
225        .expect("provider binding should build");
226
227        let mut registry = EventHandlerRegistry::default();
228        binding.register_event_handlers(&mut registry, &EventHandlerRegistrationContext::empty());
229
230        assert_eq!(registry.handler_count("identity.user_registered.v1"), 1);
231    }
232
233    #[test]
234    fn provider_binding_rejects_invalid_event_handler_name() {
235        let error = ProviderBinding::from_surfaces(
236            ProviderConfig::new("provider-crm", "http://127.0.0.1:4100/lenso/provider/v1"),
237            None,
238            Some(&EventSurface {
239                handlers: vec![EventHandlerDeclaration {
240                    name: "sync/contact".to_owned(),
241                    event_name: "identity.user_registered.v1".to_owned(),
242                    operation: None,
243                }],
244            }),
245        )
246        .expect_err("invalid event handler name should fail");
247
248        assert_eq!(error.code, platform_core::ErrorCode::Validation);
249    }
250}