Skip to content

Commit 4e94dda

Browse files
committed
WIP Bridge: js-module sender input
The general idea here is to let folks define a js module to use as a data source for a "sender." This could be general purpose but the initial use case is for polling APIs that don't expose webhooks. By using `fetch` to issue HTTP requests, we can track state in the module itself to decide if the data is worth sending a webhook for. Example: <https://github.com/svix-onelson/poller-input-poc/blob/main/fetcher.js> There are many barriers to integrating the code in the POC linked above which need to be cleared first. We need: - a custom module loader to let us source modules from the bridge config instead of js files on disk. - Glue code to connect the `op_forward` deno extension calls to either a transformation or svix sender output. - existing transformation code in bridge needs to be refactored to allow us to use "newer deno" without also introducing a memory leak. For the latter, <https://github.com/svix/monorepo-private/issues/5670> aims to solve this. In addition to the above, deno ops (i.e native extension code) are supposed to be able to register state with the runtime, but I wasn't able to get it to work for keeping track of which worker was which (allowing us to propagate payloads to the appropriate output).
1 parent c4c606b commit 4e94dda

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

bridge/svix-bridge/src/config/mod.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ use std::borrow::Cow;
33
use std::collections::HashMap;
44
use std::io::{Error, ErrorKind};
55
use std::net::SocketAddr;
6+
use std::path::PathBuf;
67
use svix_bridge_plugin_queue::config::{
78
into_receiver_output, QueueConsumerConfig, ReceiverOutputOpts as QueueOutOpts,
89
};
9-
use svix_bridge_types::{ReceiverInputOpts, ReceiverOutput, SenderInput, TransformationConfig};
10+
use svix_bridge_types::{
11+
ReceiverInputOpts, ReceiverOutput, SenderInput, SenderOutputOpts, TransformationConfig,
12+
};
1013
use tracing::Level;
1114

1215
#[derive(Deserialize)]
@@ -104,6 +107,7 @@ pub enum SenderConfig {
104107
feature = "sqs"
105108
))]
106109
QueueConsumer(QueueConsumerConfig),
110+
JsModule(JsModuleSenderConfig),
107111
}
108112

109113
impl TryFrom<SenderConfig> for Box<dyn SenderInput> {
@@ -117,6 +121,7 @@ impl TryFrom<SenderConfig> for Box<dyn SenderInput> {
117121
feature = "sqs"
118122
))]
119123
SenderConfig::QueueConsumer(backend) => backend.into_sender_input(),
124+
SenderConfig::JsModule(inner) => inner.into_sender_input(),
120125
}
121126
}
122127
}
@@ -154,5 +159,29 @@ impl ReceiverConfig {
154159
}
155160
}
156161

162+
#[derive(Deserialize)]
163+
pub struct JsModuleSenderConfig {
164+
pub name: String,
165+
pub input: JsModuleSenderInputOpts,
166+
#[serde(default)]
167+
pub transformation: Option<TransformationConfig>,
168+
pub output: SenderOutputOpts,
169+
}
170+
171+
impl JsModuleSenderConfig {
172+
fn into_sender_input(self) -> Result<Box<dyn SenderInput>, &'static str> {
173+
// FIXME: need to make it so we can use latest deno for transformations before we can
174+
// connect the new module code.
175+
todo!()
176+
}
177+
}
178+
179+
#[derive(Deserialize)]
180+
#[serde(tag = "type", rename_all = "lowercase")]
181+
pub enum JsModuleSenderInputOpts {
182+
#[serde(rename = "js-module")]
183+
JsModule { module_path: PathBuf },
184+
}
185+
157186
#[cfg(test)]
158187
mod tests;

bridge/svix-bridge/src/config/tests.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use super::Config;
2-
use crate::config::{LogFormat, LogLevel, SenderConfig};
2+
use crate::config::{
3+
JsModuleSenderConfig, JsModuleSenderInputOpts, LogFormat, LogLevel, SenderConfig,
4+
};
35
use std::collections::HashMap;
6+
use std::path::PathBuf;
47
use svix_bridge_plugin_queue::config::{QueueConsumerConfig, RabbitMqInputOpts, SenderInputOpts};
58
use svix_bridge_types::{SenderOutputOpts, SvixSenderOutputOpts};
69

@@ -485,3 +488,43 @@ fn test_variable_substitution_repeated_lookups() {
485488
panic!("sender did not match expected pattern");
486489
}
487490
}
491+
492+
#[test]
493+
fn test_js_module_sender_input_ok() {
494+
let src = r#"
495+
senders:
496+
- name: "js-module-example"
497+
input:
498+
type: "js-module"
499+
# FIXME: custom module loader needed to use yaml keys for src
500+
module_path: "./my-module.js"
501+
transformation: |
502+
function handler(input) {
503+
return {
504+
appId: "xxxxx",
505+
message: {
506+
eventType: "lipsum.word-lengths.changed",
507+
payload: { lengths: input.lengths }
508+
}
509+
};
510+
}
511+
output:
512+
type: "svix"
513+
token: "x"
514+
"#;
515+
let cfg = Config::from_src(src, Some(HashMap::new()).as_ref()).unwrap();
516+
517+
if let SenderConfig::JsModule(JsModuleSenderConfig {
518+
input: JsModuleSenderInputOpts::JsModule { module_path, .. },
519+
transformation,
520+
output: SenderOutputOpts::Svix(SvixSenderOutputOpts { token, .. }),
521+
..
522+
}) = &cfg.senders[0]
523+
{
524+
assert_eq!(module_path, &PathBuf::from("./my-module.js"));
525+
assert!(transformation.is_some());
526+
assert_eq!(token, "x");
527+
} else {
528+
panic!("sender did not match expected pattern");
529+
}
530+
}

0 commit comments

Comments
 (0)