obeli_sk_wasm_workers/
lib.rs1use concepts::StrVariant;
2use std::{error::Error, fmt::Debug};
3use utils::wasm_tools::{self};
4
5pub mod activity;
6mod component_logger;
7pub mod engines;
8pub mod epoch_ticker;
9pub mod std_output_stream;
10pub mod webhook;
11pub mod workflow;
12
13#[derive(thiserror::Error, Debug)]
14pub enum WasmFileError {
15 #[error("cannot read WASM file: {0}")]
16 CannotReadComponent(wasmtime::Error),
17 #[error("cannot decode: {0}")]
18 DecodeError(#[from] wasm_tools::DecodeError),
19 #[error("linking error - {context}, details: {err}")]
20 LinkingError {
21 context: StrVariant,
22 err: Box<dyn Error + Send + Sync>,
23 },
24}
25
26pub mod envvar {
27 #[derive(Clone, derive_more::Debug)]
28 pub struct EnvVar {
29 pub key: String,
30 #[debug(skip)]
31 pub val: String,
32 }
33}
34
35#[cfg(test)]
36pub(crate) mod tests {
37 use std::{sync::Arc, time::Duration};
38
39 use async_trait::async_trait;
40 use concepts::{
41 ComponentId, ComponentRetryConfig, FnName, FunctionFqn, FunctionMetadata, FunctionRegistry,
42 IfcFqnName, PackageIfcFns, ParameterTypes,
43 };
44 use indexmap::IndexMap;
45 use utils::wasm_tools::WasmComponent;
46
47 pub(crate) struct TestingFnRegistry {
48 ffqn_to_fn_details:
49 hashbrown::HashMap<FunctionFqn, (FunctionMetadata, ComponentId, ComponentRetryConfig)>,
50 export_hierarchy: Vec<PackageIfcFns>,
51 }
52
53 impl TestingFnRegistry {
54 pub(crate) fn new_from_components(
55 wasm_components: Vec<(WasmComponent, ComponentId)>,
56 ) -> Arc<dyn FunctionRegistry> {
57 let mut ffqn_to_fn_details = hashbrown::HashMap::new();
58 let mut export_hierarchy: hashbrown::HashMap<
59 IfcFqnName,
60 IndexMap<FnName, FunctionMetadata>,
61 > = hashbrown::HashMap::new();
62 for (wasm_component, component_id) in wasm_components {
63 for exported_function in wasm_component.exim.get_exports(true) {
64 let ffqn = exported_function.ffqn.clone();
65 ffqn_to_fn_details.insert(
66 ffqn.clone(),
67 (
68 exported_function.clone(),
69 component_id.clone(),
70 ComponentRetryConfig {
71 max_retries: 0,
72 retry_exp_backoff: Duration::ZERO,
73 },
74 ),
75 );
76
77 let index_map = export_hierarchy.entry(ffqn.ifc_fqn.clone()).or_default();
78 index_map.insert(ffqn.function_name.clone(), exported_function.clone());
79 }
80 }
81 let export_hierarchy = export_hierarchy
82 .into_iter()
83 .map(|(ifc_fqn, fns)| PackageIfcFns {
84 extension: ifc_fqn.is_extension(),
85 ifc_fqn,
86 fns,
87 })
88 .collect();
89 Arc::from(TestingFnRegistry {
90 ffqn_to_fn_details,
91 export_hierarchy,
92 })
93 }
94 }
95
96 #[async_trait]
97 impl FunctionRegistry for TestingFnRegistry {
98 async fn get_by_exported_function(
99 &self,
100 ffqn: &FunctionFqn,
101 ) -> Option<(FunctionMetadata, ComponentId, ComponentRetryConfig)> {
102 self.ffqn_to_fn_details.get(ffqn).cloned()
103 }
104
105 fn all_exports(&self) -> &[PackageIfcFns] {
106 &self.export_hierarchy
107 }
108 }
109
110 pub(crate) fn fn_registry_dummy(ffqns: &[FunctionFqn]) -> Arc<dyn FunctionRegistry> {
111 let component_id = ComponentId::dummy_activity();
112 let mut ffqn_to_fn_details = hashbrown::HashMap::new();
113 let mut export_hierarchy: hashbrown::HashMap<
114 IfcFqnName,
115 IndexMap<FnName, FunctionMetadata>,
116 > = hashbrown::HashMap::new();
117 for ffqn in ffqns {
118 let fn_metadata = FunctionMetadata {
119 ffqn: ffqn.clone(),
120 parameter_types: ParameterTypes::default(),
121 return_type: None,
122 extension: None,
123 submittable: true,
124 };
125 ffqn_to_fn_details.insert(
126 ffqn.clone(),
127 (
128 fn_metadata.clone(),
129 component_id.clone(),
130 ComponentRetryConfig {
131 max_retries: 0,
132 retry_exp_backoff: Duration::ZERO,
133 },
134 ),
135 );
136 let index_map = export_hierarchy.entry(ffqn.ifc_fqn.clone()).or_default();
137 index_map.insert(ffqn.function_name.clone(), fn_metadata);
138 }
139 let export_hierarchy = export_hierarchy
140 .into_iter()
141 .map(|(ifc_fqn, fns)| PackageIfcFns {
142 extension: ifc_fqn.is_extension(),
143 ifc_fqn,
144 fns,
145 })
146 .collect();
147 Arc::new(TestingFnRegistry {
148 ffqn_to_fn_details,
149 export_hierarchy,
150 })
151 }
152
153 mod populate_codegen_cache {
154 use crate::{
155 activity::activity_worker::tests::compile_activity,
156 workflow::workflow_worker::tests::compile_workflow,
157 };
158
159 #[rstest::rstest(wasm_path => [
160 test_programs_fibo_activity_builder::TEST_PROGRAMS_FIBO_ACTIVITY,
161 test_programs_http_get_activity_builder::TEST_PROGRAMS_HTTP_GET_ACTIVITY,
162 test_programs_sleep_activity_builder::TEST_PROGRAMS_SLEEP_ACTIVITY,
163 ])]
164 #[tokio::test]
165 async fn fibo(wasm_path: &str) {
166 compile_activity(wasm_path).await;
167 }
168
169 #[rstest::rstest(wasm_path => [
170 test_programs_fibo_workflow_builder::TEST_PROGRAMS_FIBO_WORKFLOW,
171 test_programs_http_get_workflow_builder::TEST_PROGRAMS_HTTP_GET_WORKFLOW,
172 test_programs_sleep_workflow_builder::TEST_PROGRAMS_SLEEP_WORKFLOW,
173 ])]
174 #[tokio::test]
175 async fn workflow(wasm_path: &str) {
176 compile_workflow(wasm_path).await;
177 }
178
179 #[cfg(not(madsim))]
180 #[rstest::rstest(wasm_path => [
181 test_programs_fibo_webhook_builder::TEST_PROGRAMS_FIBO_WEBHOOK
182 ])]
183 #[tokio::test]
184 async fn webhook(wasm_path: &str) {
185 crate::webhook::webhook_trigger::tests::nosim::compile_webhook(wasm_path).await;
186 }
187 }
188}