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