From f8259b7d08401fc6adebb654ec40e84942325f16 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 7 May 2024 18:25:47 +0530 Subject: [PATCH 01/25] impl WorkerIO in Rt --- examples/jsonplaceholder.graphql | 10 +- examples/scripts/echo.js | 5 + generated/.tailcallrc.graphql | 4 +- generated/.tailcallrc.schema.json | 4 +- src/blueprint/definitions.rs | 1 + src/blueprint/operators/js.rs | 53 +++++++++++ src/blueprint/operators/mod.rs | 2 + src/cli/mod.rs | 2 - src/cli/runtime/mod.rs | 2 +- src/config/config.rs | 2 +- src/{cli => }/javascript/js_request.rs | 0 src/{cli => }/javascript/js_response.rs | 0 src/{cli => }/javascript/mod.rs | 2 +- src/{cli => }/javascript/request_filter.rs | 6 +- src/{cli => }/javascript/runtime.rs | 102 +++++++++++++++++---- src/{cli => }/javascript/shim/console.js | 0 src/lambda/expression.rs | 9 +- src/lambda/modify.rs | 1 + src/lib.rs | 4 +- src/runtime.rs | 3 +- tests/core/parse.rs | 3 +- tests/core/runtime.rs | 3 +- tests/server_spec.rs | 2 +- 23 files changed, 174 insertions(+), 46 deletions(-) create mode 100644 src/blueprint/operators/js.rs rename src/{cli => }/javascript/js_request.rs (100%) rename src/{cli => }/javascript/js_response.rs (100%) rename src/{cli => }/javascript/mod.rs (92%) rename src/{cli => }/javascript/request_filter.rs (96%) rename src/{cli => }/javascript/runtime.rs (55%) rename src/{cli => }/javascript/shim/console.js (100%) diff --git a/examples/jsonplaceholder.graphql b/examples/jsonplaceholder.graphql index 028bc1b047..3c3f040128 100644 --- a/examples/jsonplaceholder.graphql +++ b/examples/jsonplaceholder.graphql @@ -1,13 +1,12 @@ schema - @server(port: 8000, headers: {cors: {allowOrigins: ["*"], allowHeaders: ["*"], allowMethods: [POST, GET, OPTIONS]}}) - @upstream(baseURL: "http://jsonplaceholder.typicode.com", httpCache: true, batch: {delay: 100}) { + @server(port: 8000) + @link(src: "scripts/echo.js", type: Script) + @upstream(baseURL: "http://jsonplaceholder.typicode.com", httpCache: true) { query: Query } type Query { posts: [Post] @http(path: "/posts") - users: [User] @http(path: "/users") - user(id: Int!): User @http(path: "/users/{{.args.id}}") } type User { @@ -24,5 +23,6 @@ type Post { userId: Int! title: String! body: String! - user: User @call(steps: [{query: "user", args: {id: "{{.value.userId}}"}}]) + curId: Int! @js(name: "hello") + # user: User @call(steps: [{query: "user", args: {id: "{{.value.userId}}"}}]) } diff --git a/examples/scripts/echo.js b/examples/scripts/echo.js index d09ec8d429..77cdcfdb38 100644 --- a/examples/scripts/echo.js +++ b/examples/scripts/echo.js @@ -1,3 +1,8 @@ function onRequest({request}) { return {request} } + +function hello(val) { + let json = JSON.parse(val) + return JSON.stringify(json.id) +} diff --git a/generated/.tailcallrc.graphql b/generated/.tailcallrc.graphql index 9b6705faeb..383e95ca5c 100644 --- a/generated/.tailcallrc.graphql +++ b/generated/.tailcallrc.graphql @@ -179,7 +179,7 @@ directive @http( ) on FIELD_DEFINITION directive @js( - script: String! + name: String! ) on FIELD_DEFINITION """ @@ -667,7 +667,7 @@ enum HttpVersion { HTTP2 } input JS { - script: String! + name: String! } input KeyValue { key: String! diff --git a/generated/.tailcallrc.schema.json b/generated/.tailcallrc.schema.json index a39e72e028..8e117c015c 100644 --- a/generated/.tailcallrc.schema.json +++ b/generated/.tailcallrc.schema.json @@ -749,10 +749,10 @@ "JS": { "type": "object", "required": [ - "script" + "name" ], "properties": { - "script": { + "name": { "type": "string" } } diff --git a/src/blueprint/definitions.rs b/src/blueprint/definitions.rs index 7a7fdb1991..19e5665add 100644 --- a/src/blueprint/definitions.rs +++ b/src/blueprint/definitions.rs @@ -500,6 +500,7 @@ pub fn to_field_definition( .and(update_http().trace(config::Http::trace_name().as_str())) .and(update_grpc(operation_type).trace(config::Grpc::trace_name().as_str())) .and(update_const_field().trace(config::Expr::trace_name().as_str())) + .and(update_js_field().trace(config::JS::trace_name().as_str())) .and(update_graphql(operation_type).trace(config::GraphQL::trace_name().as_str())) .and(update_modify().trace(config::Modify::trace_name().as_str())) .and(update_call(operation_type, object_name).trace(config::Call::trace_name().as_str())) diff --git a/src/blueprint/operators/js.rs b/src/blueprint/operators/js.rs new file mode 100644 index 0000000000..449b843fd2 --- /dev/null +++ b/src/blueprint/operators/js.rs @@ -0,0 +1,53 @@ +use std::sync::Arc; + +use crate::blueprint::*; +use crate::config; +use crate::config::Field; +use crate::javascript::Runtime; +use crate::lambda::Expression; +use crate::try_fold::TryFold; +use crate::valid::{Valid, ValidationError, Validator}; + +pub struct CompileJs<'a> { + pub name: &'a str, + pub script: &'a Option, +} + +pub fn compile_js(inputs: CompileJs) -> Valid { + let name = inputs.name; + let quickjs = rquickjs::Runtime::new().unwrap(); + let ctx = rquickjs::Context::full(&quickjs).unwrap(); + + Valid::from_option(inputs.script.as_ref(), "script is required".to_string()).and_then( + |script| { + Valid::from( + ctx.with(|ctx| { + ctx.eval::<(), &str>(script)?; + Ok::<_, anyhow::Error>(()) + }) + .map_err(|e| ValidationError::new(e.to_string())), + ) + .map(|_| { + Expression::Js(Arc::new(Runtime::new( + name.to_string(), + Script { source: script.clone(), timeout: None }, + ))) + }) + }, + ) +} + +pub fn update_js_field<'a>( +) -> TryFold<'a, (&'a ConfigModule, &'a Field, &'a config::Type, &'a str), FieldDefinition, String> +{ + TryFold::<(&ConfigModule, &Field, &config::Type, &str), FieldDefinition, String>::new( + |(module, field, _, _), b_field| { + let Some(js) = &field.script else { + return Valid::succeed(b_field); + }; + + compile_js(CompileJs { script: &module.extensions.script, name: &js.name }) + .map(|resolver| b_field.resolver(Some(resolver))) + }, + ) +} diff --git a/src/blueprint/operators/mod.rs b/src/blueprint/operators/mod.rs index f12e64780e..166d71f219 100644 --- a/src/blueprint/operators/mod.rs +++ b/src/blueprint/operators/mod.rs @@ -3,6 +3,7 @@ mod expr; mod graphql; mod grpc; mod http; +mod js; mod modify; mod protected; @@ -11,5 +12,6 @@ pub use expr::*; pub use graphql::*; pub use grpc::*; pub use http::*; +pub use js::*; pub use modify::*; pub use protected::*; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 497016fab9..bc3c987b93 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,8 +1,6 @@ mod command; mod error; mod fmt; -#[cfg(feature = "js")] -pub mod javascript; pub mod metrics; pub mod server; mod tc; diff --git a/src/cli/runtime/mod.rs b/src/cli/runtime/mod.rs index b0499c6f35..6852d5ce39 100644 --- a/src/cli/runtime/mod.rs +++ b/src/cli/runtime/mod.rs @@ -23,7 +23,7 @@ fn init_file() -> Arc { fn init_hook_http(http: Arc, script: Option) -> Arc { #[cfg(feature = "js")] if let Some(script) = script { - return crate::cli::javascript::init_http(http, script); + return crate::javascript::init_http(http, script); } #[cfg(not(feature = "js"))] diff --git a/src/config/config.rs b/src/config/config.rs index a2478f37e7..d863224f06 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -355,7 +355,7 @@ impl Field { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema)] pub struct JS { - pub script: String, + pub name: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, schemars::JsonSchema)] diff --git a/src/cli/javascript/js_request.rs b/src/javascript/js_request.rs similarity index 100% rename from src/cli/javascript/js_request.rs rename to src/javascript/js_request.rs diff --git a/src/cli/javascript/js_response.rs b/src/javascript/js_response.rs similarity index 100% rename from src/cli/javascript/js_response.rs rename to src/javascript/js_response.rs diff --git a/src/cli/javascript/mod.rs b/src/javascript/mod.rs similarity index 92% rename from src/cli/javascript/mod.rs rename to src/javascript/mod.rs index 8ecbb6aa40..90859b5f13 100644 --- a/src/cli/javascript/mod.rs +++ b/src/javascript/mod.rs @@ -20,7 +20,7 @@ pub fn init_http( script: blueprint::Script, ) -> Arc { tracing::debug!("Initializing JavaScript HTTP filter: {}", script.source); - let script_io = Arc::new(Runtime::new(script)); + let script_io = Arc::new(Runtime::new("onRequest".to_string(), script)); Arc::new(RequestFilter::new(http, script_io)) } diff --git a/src/cli/javascript/request_filter.rs b/src/javascript/request_filter.rs similarity index 96% rename from src/cli/javascript/request_filter.rs rename to src/javascript/request_filter.rs index 5cb6b653d1..1bc7c04b51 100644 --- a/src/cli/javascript/request_filter.rs +++ b/src/javascript/request_filter.rs @@ -63,7 +63,7 @@ impl RequestFilter { async fn on_request(&self, mut request: reqwest::Request) -> anyhow::Result> { let js_request = JsRequest::try_from(&request)?; let event = Event::Request(js_request); - let command = self.worker.call("onRequest".to_string(), event).await?; + let command = self.worker.call(event).await?; match command { Some(command) => match command { Command::Request(js_request) => { @@ -104,9 +104,9 @@ mod tests { use hyper::body::Bytes; use rquickjs::{Context, FromJs, IntoJs, Object, Runtime, String as JsString}; - use crate::cli::javascript::request_filter::Command; - use crate::cli::javascript::{JsRequest, JsResponse}; use crate::http::Response; + use crate::javascript::request_filter::Command; + use crate::javascript::{JsRequest, JsResponse}; #[test] fn test_command_from_invalid_object() { diff --git a/src/cli/javascript/runtime.rs b/src/javascript/runtime.rs similarity index 55% rename from src/cli/javascript/runtime.rs rename to src/javascript/runtime.rs index 3f2c5cf7b0..a5f9339371 100644 --- a/src/cli/javascript/runtime.rs +++ b/src/javascript/runtime.rs @@ -1,6 +1,8 @@ use std::cell::{OnceCell, RefCell}; +use std::fmt::{Debug, Formatter}; use std::thread; +use async_graphql_value::ConstValue; use rquickjs::{Context, Ctx, FromJs, Function, IntoJs, Value}; use super::request_filter::{Command, Event}; @@ -25,7 +27,7 @@ fn qjs_print(msg: String, is_err: bool) { fn setup_builtins(ctx: &Ctx<'_>) -> rquickjs::Result<()> { ctx.globals().set("__qjs_print", js_qjs_print)?; - let _: Value = ctx.eval_file("src/cli/javascript/shim/console.js")?; + let _: Value = ctx.eval_file("src/javascript/shim/console.js")?; Ok(()) } @@ -47,18 +49,33 @@ impl LocalRuntime { pub struct Runtime { script: blueprint::Script, + function_name: String, // Single threaded JS runtime, that's shared across all tokio workers. tokio_runtime: Option, } +impl Debug for Runtime { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Runtime {{ script: {:?}, function_name: {} }}", + self.script, self.function_name + ) + } +} + impl Runtime { - pub fn new(script: blueprint::Script) -> Self { + pub fn new(name: String, script: blueprint::Script) -> Self { let tokio_runtime = tokio::runtime::Builder::new_multi_thread() .worker_threads(1) .build() .expect("JS runtime not initialized"); - Self { script, tokio_runtime: Some(tokio_runtime) } + Self { + script, + function_name: name, + tokio_runtime: Some(tokio_runtime), + } } } @@ -75,27 +92,13 @@ impl Drop for Runtime { #[async_trait::async_trait] impl WorkerIO for Runtime { - async fn call(&self, name: String, event: Event) -> anyhow::Result> { + async fn call(&self, event: Event) -> anyhow::Result> { let script = self.script.clone(); + let name = self.function_name.clone(); if let Some(runtime) = &self.tokio_runtime { runtime .spawn(async move { - // initialize runtime if this is the first call - // exit if failed to initialize - LOCAL_RUNTIME.with(move |cell| { - if cell.borrow().get().is_none() { - LocalRuntime::try_new(script).and_then(|runtime| { - cell.borrow().set(runtime).map_err(|_| { - anyhow::anyhow!( - "trying to reinitialize an already initialized QuickJS runtime" - ) - }) - }) - } else { - Ok(()) - } - })?; - + init_rt(script)?; call(name, event) }) .await? @@ -105,6 +108,40 @@ impl WorkerIO for Runtime { } } +#[async_trait::async_trait] +impl WorkerIO, ConstValue> for Runtime { + async fn call(&self, input: Option) -> anyhow::Result> { + let script = self.script.clone(); + let name = self.function_name.clone(); + if let Some(runtime) = &self.tokio_runtime { + runtime + .spawn(async move { + init_rt(script)?; + execute_inner(name, input).map(Some) + }) + .await? + } else { + anyhow::bail!("JS Runtime is stopped") + } + } +} + +fn init_rt(script: blueprint::Script) -> anyhow::Result<()> { + // initialize runtime if this is the first call + // exit if failed to initialize + LOCAL_RUNTIME.with(move |cell| { + if cell.borrow().get().is_none() { + LocalRuntime::try_new(script).and_then(|runtime| { + cell.borrow().set(runtime).map_err(|_| { + anyhow::anyhow!("trying to reinitialize an already initialized QuickJS runtime") + }) + }) + } else { + Ok(()) + } + }) +} + fn prepare_args<'js>(ctx: &Ctx<'js>, req: JsRequest) -> rquickjs::Result<(Value<'js>,)> { let object = rquickjs::Object::new(ctx.clone())?; object.set("request", req.into_js(ctx)?)?; @@ -137,3 +174,28 @@ fn call(name: String, event: Event) -> anyhow::Result> { }) }) } + +fn execute_inner(name: String, value: Option) -> anyhow::Result { + let value = value + .map(|v| serde_json::to_string(&v)) + .ok_or(anyhow::anyhow!("No graphql value found"))??; + + LOCAL_RUNTIME.with_borrow_mut(|cell| { + let runtime = cell + .get_mut() + .ok_or(anyhow::anyhow!("JS runtime not initialized"))?; + runtime.0.with(|ctx| { + let fn_as_value = ctx + .globals() + .get::<_, rquickjs::Function>(&name) + .map_err(|_| anyhow::anyhow!("globalThis not initialized"))?; + + let function = fn_as_value + .as_function() + .ok_or(anyhow::anyhow!("`hello` is not a function"))?; + let val: String = function.call((value, )) + .map_err(|_| anyhow::anyhow!("unable to parse value from js function: {} maybe because it's not returning a string?", name,))?; + Ok::<_, anyhow::Error>(serde_json::from_str(&val)?) + }) + }) +} diff --git a/src/cli/javascript/shim/console.js b/src/javascript/shim/console.js similarity index 100% rename from src/cli/javascript/shim/console.js rename to src/javascript/shim/console.js diff --git a/src/lambda/expression.rs b/src/lambda/expression.rs index 1b050d247b..e1bb59d7ae 100644 --- a/src/lambda/expression.rs +++ b/src/lambda/expression.rs @@ -8,11 +8,12 @@ use async_graphql_value::ConstValue; use thiserror::Error; use super::{Eval, EvaluationContext, ResolverContextLike, IO}; -use crate::auth; use crate::blueprint::DynamicValue; +use crate::javascript::Runtime; use crate::json::JsonLike; use crate::lambda::cache::Cache; use crate::serde_value_ext::ValueExt; +use crate::{auth, WorkerIO}; #[derive(Clone, Debug)] pub enum Expression { @@ -22,6 +23,7 @@ pub enum Expression { Cache(Cache), Path(Box, Vec), Protect(Box), + Js(Arc), } impl Display for Expression { @@ -33,6 +35,7 @@ impl Display for Expression { Expression::Cache(_) => write!(f, "Cache"), Expression::Path(_, _) => write!(f, "Input"), Expression::Protect(expr) => write!(f, "Protected({expr})"), + Expression::Js(_) => write!(f, "Js"), } } } @@ -190,6 +193,10 @@ impl Eval for Expression { } Expression::IO(operation) => operation.eval(ctx).await, Expression::Cache(cached) => cached.eval(ctx).await, + Expression::Js(js) => { + let val = js.call(ctx.value().cloned()).await?; + Ok(val.unwrap_or(async_graphql::Value::Null)) + } } }) } diff --git a/src/lambda/modify.rs b/src/lambda/modify.rs index afd59a3443..2ce6b96329 100644 --- a/src/lambda/modify.rs +++ b/src/lambda/modify.rs @@ -37,6 +37,7 @@ impl Expression { }) } }, + Expression::Js(_) => expr, Expression::Dynamic(_) => expr, Expression::IO(_) => expr, Expression::Cache(Cache { expr, max_age }) => { diff --git a/src/lib.rs b/src/lib.rs index 6ab74992b2..a54d731180 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ pub mod grpc; pub mod has_headers; pub mod helpers; pub mod http; +#[cfg(feature = "js")] +pub mod javascript; pub mod json; pub mod lambda; pub mod merge_right; @@ -85,7 +87,7 @@ pub type EntityCache = dyn Cache; #[async_trait::async_trait] pub trait WorkerIO: Send + Sync + 'static { /// Calls a global JS function - async fn call(&self, name: String, input: In) -> anyhow::Result>; + async fn call(&self, input: In) -> anyhow::Result>; } pub fn is_default(val: &T) -> bool { diff --git a/src/runtime.rs b/src/runtime.rs index fae12cd71e..1a751fa8bf 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -50,10 +50,9 @@ pub mod test { use crate::blueprint::Upstream; use crate::cache::InMemoryCache; - use crate::cli::javascript; use crate::http::Response; use crate::runtime::TargetRuntime; - use crate::{blueprint, EnvIO, FileIO, HttpIO}; + use crate::{blueprint, javascript, EnvIO, FileIO, HttpIO}; #[derive(Clone)] struct TestHttp { diff --git a/tests/core/parse.rs b/tests/core/parse.rs index 303744f300..2076da9dc0 100644 --- a/tests/core/parse.rs +++ b/tests/core/parse.rs @@ -12,11 +12,10 @@ use markdown::mdast::Node; use markdown::ParseOptions; use tailcall::blueprint::Blueprint; use tailcall::cache::InMemoryCache; -use tailcall::cli::javascript; use tailcall::config::{ConfigModule, Source}; use tailcall::http::AppContext; use tailcall::runtime::TargetRuntime; -use tailcall::EnvIO; +use tailcall::{javascript, EnvIO}; use super::file::File; use super::http::Http; diff --git a/tests/core/runtime.rs b/tests/core/runtime.rs index 0b198a4796..f9e6125b00 100644 --- a/tests/core/runtime.rs +++ b/tests/core/runtime.rs @@ -6,11 +6,10 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use derive_setters::Setters; -use tailcall::blueprint; use tailcall::cache::InMemoryCache; -use tailcall::cli::javascript; use tailcall::config::Source; use tailcall::runtime::TargetRuntime; +use tailcall::{blueprint, javascript}; use super::env::Env; use super::file::TestFileIO; diff --git a/tests/server_spec.rs b/tests/server_spec.rs index d24fbe3387..2b3ca62614 100644 --- a/tests/server_spec.rs +++ b/tests/server_spec.rs @@ -13,8 +13,8 @@ pub mod test { use reqwest::Client; use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use tailcall::cache::InMemoryCache; - use tailcall::cli::javascript; use tailcall::http::Response; + use tailcall::javascript; use tailcall::runtime::TargetRuntime; use tokio::io::{AsyncReadExt, AsyncWriteExt}; From 5276715575fbed78dca5ebaf63c7835caa802d01 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 7 May 2024 20:59:22 +0530 Subject: [PATCH 02/25] fix wasm build --- Cargo.lock | 259 ++++++------------ Cargo.toml | 2 +- src/blueprint/operators/js.rs | 23 +- src/javascript/{ => enable_js}/js_request.rs | 0 src/javascript/{ => enable_js}/js_response.rs | 0 src/javascript/enable_js/mod.rs | 40 +++ .../{ => enable_js}/request_filter.rs | 2 +- src/javascript/{ => enable_js}/runtime.rs | 0 src/javascript/mod.rs | 46 +--- src/javascript/runtime_no_js.rs | 19 ++ src/lib.rs | 1 - 11 files changed, 169 insertions(+), 223 deletions(-) rename src/javascript/{ => enable_js}/js_request.rs (100%) rename src/javascript/{ => enable_js}/js_response.rs (100%) create mode 100644 src/javascript/enable_js/mod.rs rename src/javascript/{ => enable_js}/request_filter.rs (98%) rename src/javascript/{ => enable_js}/runtime.rs (100%) create mode 100644 src/javascript/runtime_no_js.rs diff --git a/Cargo.lock b/Cargo.lock index 7f74825761..666df67a5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,7 +183,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote 1.0.36", + "quote", "syn 1.0.109", ] @@ -288,8 +288,8 @@ dependencies = [ "async-graphql-parser", "darling 0.20.8", "proc-macro-crate", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "strum 0.26.2", "syn 2.0.61", "thiserror", @@ -443,8 +443,8 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -511,8 +511,8 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -528,8 +528,8 @@ version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -894,8 +894,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -1153,8 +1153,8 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "strsim 0.10.0", "syn 1.0.109", ] @@ -1167,8 +1167,8 @@ checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "strsim 0.10.0", "syn 2.0.61", ] @@ -1180,7 +1180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.36", + "quote", "syn 1.0.109", ] @@ -1191,7 +1191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", - "quote 1.0.36", + "quote", "syn 2.0.61", ] @@ -1251,8 +1251,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ec317cc3e7ef0928b0ca6e4a634a4d6c001672ae210438cf114a83e56b018d" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -1273,8 +1273,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" dependencies = [ "darling 0.20.8", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -1345,29 +1345,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dlopen" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" -dependencies = [ - "dlopen_derive", - "lazy_static", - "libc", - "winapi", -] - -[[package]] -name = "dlopen_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" -dependencies = [ - "libc", - "quote 0.6.13", - "syn 0.15.44", -] - [[package]] name = "dotenvy" version = "0.15.7" @@ -1659,8 +1636,8 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -2554,7 +2531,7 @@ dependencies = [ "string_cache", "term", "tiny-keccak", - "unicode-xid 0.2.4", + "unicode-xid", "walkdir", ] @@ -2790,8 +2767,8 @@ checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" dependencies = [ "beef", "fnv", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "regex-syntax 0.6.29", "syn 2.0.61", ] @@ -2805,8 +2782,8 @@ dependencies = [ "beef", "fnv", "lazy_static", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "regex-syntax 0.8.3", "syn 2.0.61", ] @@ -2931,8 +2908,8 @@ version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -3453,8 +3430,8 @@ checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -3479,25 +3456,6 @@ dependencies = [ "indexmap 2.2.6", ] -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared 0.11.2", - "rand", -] - [[package]] name = "phf_shared" version = "0.10.0" @@ -3507,15 +3465,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - [[package]] name = "phonenumber" version = "0.3.4+8.13.34" @@ -3558,8 +3507,8 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -3679,7 +3628,7 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2", "syn 2.0.61", ] @@ -3700,8 +3649,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", "version_check", ] @@ -3712,20 +3661,11 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "version_check", ] -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - [[package]] name = "proc-macro2" version = "1.0.82" @@ -3789,8 +3729,8 @@ checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -4003,22 +3943,13 @@ dependencies = [ "memchr", ] -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2", ] [[package]] @@ -4252,8 +4183,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad7f63201fa6f2ff8173e4758ea552549d687d8f63003361a8b5c50f7c446ded" dependencies = [ - "either", - "indexmap 2.2.6", "rquickjs-core", "rquickjs-macro", ] @@ -4264,11 +4193,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cad00eeddc0f88af54ee202c8385fb214fe0423897c056a7df8369fb482e3695" dependencies = [ - "chrono", - "dlopen", - "either", - "indexmap 2.2.6", - "phf", "relative-path", "rquickjs-sys", ] @@ -4283,12 +4207,10 @@ dependencies = [ "fnv", "ident_case", "indexmap 2.2.6", - "phf_generator", - "phf_shared 0.11.2", "proc-macro-crate", "proc-macro-error", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "rquickjs-core", "syn 2.0.61", ] @@ -4490,8 +4412,8 @@ version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "serde_derive_internals", "syn 2.0.61", ] @@ -4603,8 +4525,8 @@ version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -4614,8 +4536,8 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -4860,7 +4782,7 @@ dependencies = [ "new_debug_unreachable", "once_cell", "parking_lot", - "phf_shared 0.10.0", + "phf_shared", "precomputed-hash", ] @@ -4907,8 +4829,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "rustversion", "syn 2.0.61", ] @@ -4920,8 +4842,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "rustversion", "syn 2.0.61", ] @@ -4932,25 +4854,14 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", -] - [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "unicode-ident", ] @@ -4960,8 +4871,8 @@ version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "unicode-ident", ] @@ -5179,8 +5090,8 @@ dependencies = [ name = "tailcall-macros" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -5305,8 +5216,8 @@ version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -5429,8 +5340,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -5548,9 +5459,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ "prettyplease", - "proc-macro2 1.0.82", + "proc-macro2", "prost-build", - "quote 1.0.36", + "quote", "syn 2.0.61", ] @@ -5561,9 +5472,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" dependencies = [ "prettyplease", - "proc-macro2 1.0.82", + "proc-macro2", "prost-build", - "quote 1.0.36", + "quote", "syn 2.0.61", ] @@ -5660,8 +5571,8 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -5836,12 +5747,6 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "unicode-xid" version = "0.2.4" @@ -5975,8 +5880,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", "wasm-bindgen-shared", ] @@ -5999,7 +5904,7 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote 1.0.36", + "quote", "wasm-bindgen-macro-support", ] @@ -6009,8 +5914,8 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -6342,8 +6247,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a0f7f15151a77dca96813d0eff10ab9b29114533fae0267d00c466c13081e69" dependencies = [ "async-trait", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", "wasm-bindgen", "wasm-bindgen-futures", @@ -6370,8 +6275,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e" dependencies = [ "darling 0.20.8", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] @@ -6396,8 +6301,8 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 2.0.61", ] diff --git a/Cargo.toml b/Cargo.toml index 3fcf38d835..19144a294b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,7 +96,7 @@ lazy_static = "1.4.0" which = { version = "6.0.1", optional = true } async-recursion = "1.1.1" tempfile = "3.10.1" -rquickjs = { "version" = "0.5.1", optional = true, features = ["full"] } +rquickjs = { "version" = "0.5.1", optional = true, features = ["macro"] } strum_macros = "0.26.2" # TODO: disable some levels with features? tracing = "0.1.40" diff --git a/src/blueprint/operators/js.rs b/src/blueprint/operators/js.rs index 449b843fd2..80907356c5 100644 --- a/src/blueprint/operators/js.rs +++ b/src/blueprint/operators/js.rs @@ -1,12 +1,9 @@ -use std::sync::Arc; - use crate::blueprint::*; use crate::config; use crate::config::Field; -use crate::javascript::Runtime; use crate::lambda::Expression; use crate::try_fold::TryFold; -use crate::valid::{Valid, ValidationError, Validator}; +use crate::valid::{Valid, Validator}; pub struct CompileJs<'a> { pub name: &'a str, @@ -14,6 +11,20 @@ pub struct CompileJs<'a> { } pub fn compile_js(inputs: CompileJs) -> Valid { + #[cfg(not(feature = "js"))] + return js_disabled(inputs); + + #[cfg(feature = "js")] + js_enabled(inputs) +} + +#[cfg(not(feature = "js"))] +fn js_disabled(_: CompileJs) -> Valid { + Valid::fail("js is disabled or unsupported on this platform".to_string()) +} + +#[cfg(feature = "js")] +fn js_enabled(inputs: CompileJs) -> Valid { let name = inputs.name; let quickjs = rquickjs::Runtime::new().unwrap(); let ctx = rquickjs::Context::full(&quickjs).unwrap(); @@ -25,10 +36,10 @@ pub fn compile_js(inputs: CompileJs) -> Valid { ctx.eval::<(), &str>(script)?; Ok::<_, anyhow::Error>(()) }) - .map_err(|e| ValidationError::new(e.to_string())), + .map_err(|e| crate::valid::ValidationError::new(e.to_string())), ) .map(|_| { - Expression::Js(Arc::new(Runtime::new( + Expression::Js(std::sync::Arc::new(crate::javascript::Runtime::new( name.to_string(), Script { source: script.clone(), timeout: None }, ))) diff --git a/src/javascript/js_request.rs b/src/javascript/enable_js/js_request.rs similarity index 100% rename from src/javascript/js_request.rs rename to src/javascript/enable_js/js_request.rs diff --git a/src/javascript/js_response.rs b/src/javascript/enable_js/js_response.rs similarity index 100% rename from src/javascript/js_response.rs rename to src/javascript/enable_js/js_response.rs diff --git a/src/javascript/enable_js/mod.rs b/src/javascript/enable_js/mod.rs new file mode 100644 index 0000000000..a3a8bc0429 --- /dev/null +++ b/src/javascript/enable_js/mod.rs @@ -0,0 +1,40 @@ +use std::collections::BTreeMap; +pub use std::sync::Arc; + +use hyper::header::{HeaderName, HeaderValue}; + +mod js_request; + +mod js_response; + +mod request_filter; + +mod runtime; + +pub use js_request::JsRequest; +pub use js_response::JsResponse; +pub use request_filter::RequestFilter; +pub use runtime::Runtime; + +use crate::{blueprint, HttpIO}; + +pub fn init_http( + http: Arc, + script: blueprint::Script, +) -> Arc { + tracing::debug!("Initializing JavaScript HTTP filter: {}", script.source); + let script_io = Arc::new(Runtime::new("onRequest".to_string(), script)); + Arc::new(RequestFilter::new(http, script_io)) +} + +fn create_header_map( + headers: BTreeMap, +) -> anyhow::Result { + let mut header_map = reqwest::header::HeaderMap::new(); + for (key, value) in headers.iter() { + let key = HeaderName::from_bytes(key.as_bytes())?; + let value = HeaderValue::from_str(value.as_str())?; + header_map.insert(key, value); + } + Ok(header_map) +} diff --git a/src/javascript/request_filter.rs b/src/javascript/enable_js/request_filter.rs similarity index 98% rename from src/javascript/request_filter.rs rename to src/javascript/enable_js/request_filter.rs index 1bc7c04b51..aad5b21b7c 100644 --- a/src/javascript/request_filter.rs +++ b/src/javascript/enable_js/request_filter.rs @@ -105,7 +105,7 @@ mod tests { use rquickjs::{Context, FromJs, IntoJs, Object, Runtime, String as JsString}; use crate::http::Response; - use crate::javascript::request_filter::Command; + use crate::javascript::enable_js::request_filter::Command; use crate::javascript::{JsRequest, JsResponse}; #[test] diff --git a/src/javascript/runtime.rs b/src/javascript/enable_js/runtime.rs similarity index 100% rename from src/javascript/runtime.rs rename to src/javascript/enable_js/runtime.rs diff --git a/src/javascript/mod.rs b/src/javascript/mod.rs index 90859b5f13..8b4fa230f9 100644 --- a/src/javascript/mod.rs +++ b/src/javascript/mod.rs @@ -1,37 +1,9 @@ -use std::collections::BTreeMap; -pub use std::sync::Arc; - -use hyper::header::{HeaderName, HeaderValue}; - -mod js_request; -mod js_response; -mod request_filter; -mod runtime; - -pub use js_request::JsRequest; -pub use js_response::JsResponse; -pub use request_filter::RequestFilter; -pub use runtime::Runtime; - -use crate::{blueprint, HttpIO}; - -pub fn init_http( - http: Arc, - script: blueprint::Script, -) -> Arc { - tracing::debug!("Initializing JavaScript HTTP filter: {}", script.source); - let script_io = Arc::new(Runtime::new("onRequest".to_string(), script)); - Arc::new(RequestFilter::new(http, script_io)) -} - -fn create_header_map( - headers: BTreeMap, -) -> anyhow::Result { - let mut header_map = reqwest::header::HeaderMap::new(); - for (key, value) in headers.iter() { - let key = HeaderName::from_bytes(key.as_bytes())?; - let value = HeaderValue::from_str(value.as_str())?; - header_map.insert(key, value); - } - Ok(header_map) -} +#[cfg(feature = "js")] +mod enable_js; +#[cfg(feature = "js")] +pub use enable_js::*; + +#[cfg(not(feature = "js"))] +mod runtime_no_js; +#[cfg(not(feature = "js"))] +pub use runtime_no_js::*; diff --git a/src/javascript/runtime_no_js.rs b/src/javascript/runtime_no_js.rs new file mode 100644 index 0000000000..f94f85eca3 --- /dev/null +++ b/src/javascript/runtime_no_js.rs @@ -0,0 +1,19 @@ +use async_graphql_value::ConstValue; + +use crate::{blueprint, WorkerIO}; + +#[derive(Debug)] +pub struct Runtime; + +impl Runtime { + pub fn new(_: String, _: blueprint::Script) -> Self { + panic!("JavaScript runtime is not supported in this build") + } +} + +#[async_trait::async_trait] +impl WorkerIO, ConstValue> for Runtime { + async fn call(&self, _: Option) -> anyhow::Result> { + panic!("JavaScript runtime is not supported in this build") + } +} diff --git a/src/lib.rs b/src/lib.rs index a54d731180..7eab82fd9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,6 @@ pub mod grpc; pub mod has_headers; pub mod helpers; pub mod http; -#[cfg(feature = "js")] pub mod javascript; pub mod json; pub mod lambda; From fc382843e1220385227ab4b5539fca215fbeb645 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Wed, 8 May 2024 09:59:38 +0530 Subject: [PATCH 03/25] undo changes in example --- examples/jsonplaceholder.graphql | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/jsonplaceholder.graphql b/examples/jsonplaceholder.graphql index 3c3f040128..028bc1b047 100644 --- a/examples/jsonplaceholder.graphql +++ b/examples/jsonplaceholder.graphql @@ -1,12 +1,13 @@ schema - @server(port: 8000) - @link(src: "scripts/echo.js", type: Script) - @upstream(baseURL: "http://jsonplaceholder.typicode.com", httpCache: true) { + @server(port: 8000, headers: {cors: {allowOrigins: ["*"], allowHeaders: ["*"], allowMethods: [POST, GET, OPTIONS]}}) + @upstream(baseURL: "http://jsonplaceholder.typicode.com", httpCache: true, batch: {delay: 100}) { query: Query } type Query { posts: [Post] @http(path: "/posts") + users: [User] @http(path: "/users") + user(id: Int!): User @http(path: "/users/{{.args.id}}") } type User { @@ -23,6 +24,5 @@ type Post { userId: Int! title: String! body: String! - curId: Int! @js(name: "hello") - # user: User @call(steps: [{query: "user", args: {id: "{{.value.userId}}"}}]) + user: User @call(steps: [{query: "user", args: {id: "{{.value.userId}}"}}]) } From 701052c86e689344e1ae7d6922eeade0821fe1aa Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Thu, 9 May 2024 00:28:01 +0530 Subject: [PATCH 04/25] move Js to Expression::IO --- Cargo.lock | 1 + benches/data_loader_bench.rs | 28 ++++++++++++---- ...impl_path_string_for_evaluation_context.rs | 28 +++++++++++++++- src/app_context.rs | 3 ++ src/blueprint/operators/js.rs | 7 +--- src/cli/runtime/mod.rs | 15 ++++++++- src/javascript/enable_js/js_request.rs | 4 +-- src/javascript/enable_js/js_response.rs | 4 +-- src/javascript/enable_js/mod.rs | 8 ++--- src/javascript/enable_js/request_filter.rs | 15 ++------- src/javascript/enable_js/runtime.rs | 28 ++++++---------- src/javascript/mod.rs | 33 +++++++++++++++++++ src/javascript/runtime_no_js.rs | 4 +-- src/lambda/expression.rs | 9 +---- src/lambda/io.rs | 21 ++++++++++++ src/lambda/modify.rs | 1 - src/lib.rs | 2 +- src/runtime.rs | 30 +++++++++++++++-- tailcall-aws-lambda/Cargo.toml | 1 + tailcall-aws-lambda/src/main.rs | 1 + tailcall-aws-lambda/src/runtime.rs | 3 ++ tailcall-aws-lambda/src/worker_io.rs | 25 ++++++++++++++ tailcall-cloudflare/src/lib.rs | 1 + tailcall-cloudflare/src/runtime.rs | 4 ++- tailcall-cloudflare/src/worker_io.rs | 25 ++++++++++++++ tests/core/mod.rs | 1 + tests/core/parse.rs | 3 ++ tests/core/runtime.rs | 3 ++ tests/core/worker_io.rs | 25 ++++++++++++++ tests/server_spec.rs | 32 +++++++++++++++++- 30 files changed, 293 insertions(+), 72 deletions(-) create mode 100644 tailcall-aws-lambda/src/worker_io.rs create mode 100644 tailcall-cloudflare/src/worker_io.rs create mode 100644 tests/core/worker_io.rs diff --git a/Cargo.lock b/Cargo.lock index 666df67a5e..60167f61c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5043,6 +5043,7 @@ name = "tailcall-aws-lambda" version = "0.1.0" dependencies = [ "anyhow", + "async-graphql-value", "async-trait", "dotenvy", "hyper 0.14.28", diff --git a/benches/data_loader_bench.rs b/benches/data_loader_bench.rs index 59b47a49e9..2e53501fe2 100644 --- a/benches/data_loader_bench.rs +++ b/benches/data_loader_bench.rs @@ -6,14 +6,14 @@ use std::sync::Arc; use async_graphql::futures_util::future::join_all; use async_graphql_value::ConstValue; -use async_trait::async_trait; use criterion::Criterion; use hyper::body::Bytes; use reqwest::Request; use tailcall::config::Batch; use tailcall::http::{DataLoaderRequest, HttpDataLoader, Response}; +use tailcall::javascript::{Command, Event}; use tailcall::runtime::TargetRuntime; -use tailcall::{EnvIO, FileIO, HttpIO}; +use tailcall::{EnvIO, FileIO, HttpIO, WorkerIO}; #[derive(Clone)] struct MockHttpClient { @@ -29,7 +29,6 @@ impl HttpIO for MockHttpClient { } struct Env {} -#[async_trait] impl EnvIO for Env { fn get(&self, _: &str) -> Option> { unimplemented!("Not needed for this bench") @@ -38,7 +37,22 @@ impl EnvIO for Env { struct File; -#[async_trait] +struct JsRt; +#[async_trait::async_trait] +impl WorkerIO for JsRt { + async fn call(&self, _: String, _: Event) -> anyhow::Result> { + unimplemented!("Not needed for this bench") + } +} + +#[async_trait::async_trait] +impl WorkerIO, ConstValue> for JsRt { + async fn call(&self, _: String, _: Option) -> anyhow::Result> { + unimplemented!("Not needed for this bench") + } +} + +#[async_trait::async_trait] impl FileIO for File { async fn write<'a>(&'a self, _: &'a str, _: &'a [u8]) -> anyhow::Result<()> { unimplemented!("Not needed for this bench") @@ -50,7 +64,7 @@ impl FileIO for File { } struct Cache; -#[async_trait] +#[async_trait::async_trait] impl tailcall::Cache for Cache { type Key = u64; type Value = ConstValue; @@ -60,7 +74,7 @@ impl tailcall::Cache for Cache { } async fn get<'a>(&'a self, _: &'a Self::Key) -> anyhow::Result> { - unimplemented!("Not needed for this bench") + unimplemented!("Nwot needed for this bench") } fn hit_rate(&self) -> Option { @@ -81,6 +95,8 @@ pub fn benchmark_data_loader(c: &mut Criterion) { file: Arc::new(File {}), cache: Arc::new(Cache {}), extensions: Arc::new(vec![]), + http_worker: Arc::new(JsRt {}), + resolver_worker: Arc::new(JsRt {}), }; let loader = HttpDataLoader::new(rt, None, false); let loader = loader.to_data_loader(Batch::default().delay(1)); diff --git a/benches/impl_path_string_for_evaluation_context.rs b/benches/impl_path_string_for_evaluation_context.rs index fbd622f03e..32c09457b8 100644 --- a/benches/impl_path_string_for_evaluation_context.rs +++ b/benches/impl_path_string_for_evaluation_context.rs @@ -5,6 +5,7 @@ use std::time::Duration; use async_graphql::context::SelectionField; use async_graphql::{Name, Value}; +use async_graphql_value::ConstValue; use async_trait::async_trait; use criterion::{BenchmarkId, Criterion}; use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions, MokaManager}; @@ -18,10 +19,33 @@ use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use tailcall::blueprint::{Server, Upstream}; use tailcall::cache::InMemoryCache; use tailcall::http::{RequestContext, Response}; +use tailcall::javascript::{Command, Event}; use tailcall::lambda::{EvaluationContext, ResolverContextLike}; use tailcall::path::PathString; use tailcall::runtime::TargetRuntime; -use tailcall::{EnvIO, FileIO, HttpIO}; +use tailcall::{EnvIO, FileIO, HttpIO, WorkerIO}; + +pub struct JsRuntime {} + +impl JsRuntime { + pub fn init() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl WorkerIO for JsRuntime { + async fn call(&self, _: String, _: Event) -> anyhow::Result> { + todo!() + } +} + +#[async_trait::async_trait] +impl WorkerIO, ConstValue> for JsRuntime { + async fn call(&self, _: String, _: Option) -> anyhow::Result> { + todo!() + } +} struct Http { client: ClientWithMiddleware, @@ -245,6 +269,8 @@ fn request_context() -> RequestContext { file: Arc::new(File {}), cache: Arc::new(InMemoryCache::new()), extensions: Arc::new(vec![]), + http_worker: Arc::new(JsRuntime::init()), + resolver_worker: Arc::new(JsRuntime::init()), }; RequestContext::new(runtime) .server(server) diff --git a/src/app_context.rs b/src/app_context.rs index 82bca0b3de..e6e8f393e4 100644 --- a/src/app_context.rs +++ b/src/app_context.rs @@ -101,6 +101,9 @@ impl AppContext { result } + IO::Js { name: method } => { + Some(Expression::IO(IO::Js { name: method.clone() })) + } }, _ => None, }) diff --git a/src/blueprint/operators/js.rs b/src/blueprint/operators/js.rs index 80907356c5..f155bf8d0d 100644 --- a/src/blueprint/operators/js.rs +++ b/src/blueprint/operators/js.rs @@ -38,12 +38,7 @@ fn js_enabled(inputs: CompileJs) -> Valid { }) .map_err(|e| crate::valid::ValidationError::new(e.to_string())), ) - .map(|_| { - Expression::Js(std::sync::Arc::new(crate::javascript::Runtime::new( - name.to_string(), - Script { source: script.clone(), timeout: None }, - ))) - }) + .map(|_| Expression::IO(crate::lambda::IO::Js { name: name.to_string() })) }, ) } diff --git a/src/cli/runtime/mod.rs b/src/cli/runtime/mod.rs index 6852d5ce39..2426c20944 100644 --- a/src/cli/runtime/mod.rs +++ b/src/cli/runtime/mod.rs @@ -7,8 +7,9 @@ use std::sync::Arc; use crate::blueprint::Blueprint; use crate::cache::InMemoryCache; +use crate::javascript::{Command, Event}; use crate::runtime::TargetRuntime; -use crate::{blueprint, EnvIO, FileIO, HttpIO}; +use crate::{blueprint, EnvIO, FileIO, HttpIO, WorkerIO}; // Provides access to env in native rust environment fn init_env() -> Arc { @@ -33,6 +34,16 @@ fn init_hook_http(http: Arc, script: Option) -> http } +fn init_http_worker_io(script: Option) -> Arc> { + crate::javascript::init_rt(script) +} + +fn init_resolver_worker_io( + script: Option, +) -> Arc, async_graphql::Value>> { + crate::javascript::init_rt(script) +} + // Provides access to http in native rust environment fn init_http(blueprint: &Blueprint) -> Arc { let http_io = http::NativeHttp::init(&blueprint.upstream, &blueprint.telemetry); @@ -60,5 +71,7 @@ pub fn init(blueprint: &Blueprint) -> TargetRuntime { file: init_file(), cache: Arc::new(init_in_memory_cache()), extensions: Arc::new(vec![]), + http_worker: init_http_worker_io(blueprint.server.script.clone()), + resolver_worker: init_resolver_worker_io(blueprint.server.script.clone()), } } diff --git a/src/javascript/enable_js/js_request.rs b/src/javascript/enable_js/js_request.rs index c2601ed326..9fb3c9435c 100644 --- a/src/javascript/enable_js/js_request.rs +++ b/src/javascript/enable_js/js_request.rs @@ -9,9 +9,7 @@ use rquickjs::{FromJs, IntoJs}; use serde::{Deserialize, Serialize}; use crate::is_default; - -#[derive(Debug)] -pub struct JsRequest(reqwest::Request); +use crate::javascript::JsRequest; impl JsRequest { fn uri(&self) -> Uri { diff --git a/src/javascript/enable_js/js_response.rs b/src/javascript/enable_js/js_response.rs index 9fd71f0797..c99bd5f142 100644 --- a/src/javascript/enable_js/js_response.rs +++ b/src/javascript/enable_js/js_response.rs @@ -5,9 +5,7 @@ use rquickjs::{FromJs, IntoJs}; use super::create_header_map; use crate::http::Response; - -#[derive(Debug)] -pub struct JsResponse(Response); +use crate::javascript::JsResponse; impl JsResponse { pub fn status(&self) -> u16 { diff --git a/src/javascript/enable_js/mod.rs b/src/javascript/enable_js/mod.rs index a3a8bc0429..23300c3bb5 100644 --- a/src/javascript/enable_js/mod.rs +++ b/src/javascript/enable_js/mod.rs @@ -1,5 +1,5 @@ use std::collections::BTreeMap; -pub use std::sync::Arc; +use std::sync::Arc; use hyper::header::{HeaderName, HeaderValue}; @@ -7,12 +7,10 @@ mod js_request; mod js_response; -mod request_filter; +pub mod request_filter; mod runtime; -pub use js_request::JsRequest; -pub use js_response::JsResponse; pub use request_filter::RequestFilter; pub use runtime::Runtime; @@ -23,7 +21,7 @@ pub fn init_http( script: blueprint::Script, ) -> Arc { tracing::debug!("Initializing JavaScript HTTP filter: {}", script.source); - let script_io = Arc::new(Runtime::new("onRequest".to_string(), script)); + let script_io = Arc::new(Runtime::new(script)); Arc::new(RequestFilter::new(http, script_io)) } diff --git a/src/javascript/enable_js/request_filter.rs b/src/javascript/enable_js/request_filter.rs index aad5b21b7c..4711c48478 100644 --- a/src/javascript/enable_js/request_filter.rs +++ b/src/javascript/enable_js/request_filter.rs @@ -3,21 +3,10 @@ use std::sync::Arc; use hyper::body::Bytes; use rquickjs::FromJs; -use super::{JsRequest, JsResponse}; use crate::http::Response; +use crate::javascript::{Command, Event, JsRequest, JsResponse}; use crate::{HttpIO, WorkerIO}; -#[derive(Debug)] -pub enum Event { - Request(JsRequest), -} - -#[derive(Debug)] -pub enum Command { - Request(JsRequest), - Response(JsResponse), -} - impl<'js> FromJs<'js> for Command { fn from_js(ctx: &rquickjs::Ctx<'js>, value: rquickjs::Value<'js>) -> rquickjs::Result { let object = value.as_object().ok_or(rquickjs::Error::FromJs { @@ -63,7 +52,7 @@ impl RequestFilter { async fn on_request(&self, mut request: reqwest::Request) -> anyhow::Result> { let js_request = JsRequest::try_from(&request)?; let event = Event::Request(js_request); - let command = self.worker.call(event).await?; + let command = self.worker.call("onRequest".to_string(), event).await?; match command { Some(command) => match command { Command::Request(js_request) => { diff --git a/src/javascript/enable_js/runtime.rs b/src/javascript/enable_js/runtime.rs index a5f9339371..65c8de0aa2 100644 --- a/src/javascript/enable_js/runtime.rs +++ b/src/javascript/enable_js/runtime.rs @@ -5,8 +5,7 @@ use std::thread; use async_graphql_value::ConstValue; use rquickjs::{Context, Ctx, FromJs, Function, IntoJs, Value}; -use super::request_filter::{Command, Event}; -use super::JsRequest; +use crate::javascript::{Command, Event, JsRequest}; use crate::{blueprint, WorkerIO}; struct LocalRuntime(Context); @@ -49,33 +48,24 @@ impl LocalRuntime { pub struct Runtime { script: blueprint::Script, - function_name: String, // Single threaded JS runtime, that's shared across all tokio workers. tokio_runtime: Option, } impl Debug for Runtime { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Runtime {{ script: {:?}, function_name: {} }}", - self.script, self.function_name - ) + write!(f, "Runtime {{ script: {:?} }}", self.script) } } impl Runtime { - pub fn new(name: String, script: blueprint::Script) -> Self { + pub fn new(script: blueprint::Script) -> Self { let tokio_runtime = tokio::runtime::Builder::new_multi_thread() .worker_threads(1) .build() .expect("JS runtime not initialized"); - Self { - script, - function_name: name, - tokio_runtime: Some(tokio_runtime), - } + Self { script, tokio_runtime: Some(tokio_runtime) } } } @@ -92,9 +82,8 @@ impl Drop for Runtime { #[async_trait::async_trait] impl WorkerIO for Runtime { - async fn call(&self, event: Event) -> anyhow::Result> { + async fn call(&self, name: String, event: Event) -> anyhow::Result> { let script = self.script.clone(); - let name = self.function_name.clone(); if let Some(runtime) = &self.tokio_runtime { runtime .spawn(async move { @@ -110,9 +99,12 @@ impl WorkerIO for Runtime { #[async_trait::async_trait] impl WorkerIO, ConstValue> for Runtime { - async fn call(&self, input: Option) -> anyhow::Result> { + async fn call( + &self, + name: String, + input: Option, + ) -> anyhow::Result> { let script = self.script.clone(); - let name = self.function_name.clone(); if let Some(runtime) = &self.tokio_runtime { runtime .spawn(async move { diff --git a/src/javascript/mod.rs b/src/javascript/mod.rs index 8b4fa230f9..324500270a 100644 --- a/src/javascript/mod.rs +++ b/src/javascript/mod.rs @@ -1,5 +1,8 @@ #[cfg(feature = "js")] mod enable_js; + +use std::sync::Arc; + #[cfg(feature = "js")] pub use enable_js::*; @@ -7,3 +10,33 @@ pub use enable_js::*; mod runtime_no_js; #[cfg(not(feature = "js"))] pub use runtime_no_js::*; + +#[derive(Debug)] +pub struct JsResponse(Response); +#[derive(Debug)] +pub struct JsRequest(reqwest::Request); + +#[derive(Debug)] +pub enum Event { + Request(JsRequest), +} + +#[derive(Debug)] +pub enum Command { + Request(JsRequest), + Response(JsResponse), +} + +use crate::blueprint::Script; +use crate::http::Response; + +pub fn init_rt(script: Option) -> Arc { + if let Some(script) = script { + Arc::new(Runtime::new(script)) + } else { + Arc::new(Runtime::new(Script { + source: "".to_string(), + timeout: None, + })) // TODO + } +} diff --git a/src/javascript/runtime_no_js.rs b/src/javascript/runtime_no_js.rs index f94f85eca3..6093316dd9 100644 --- a/src/javascript/runtime_no_js.rs +++ b/src/javascript/runtime_no_js.rs @@ -6,14 +6,14 @@ use crate::{blueprint, WorkerIO}; pub struct Runtime; impl Runtime { - pub fn new(_: String, _: blueprint::Script) -> Self { + pub fn new(_: blueprint::Script) -> Self { panic!("JavaScript runtime is not supported in this build") } } #[async_trait::async_trait] impl WorkerIO, ConstValue> for Runtime { - async fn call(&self, _: Option) -> anyhow::Result> { + async fn call(&self, _: String, _: Option) -> anyhow::Result> { panic!("JavaScript runtime is not supported in this build") } } diff --git a/src/lambda/expression.rs b/src/lambda/expression.rs index e1bb59d7ae..1b050d247b 100644 --- a/src/lambda/expression.rs +++ b/src/lambda/expression.rs @@ -8,12 +8,11 @@ use async_graphql_value::ConstValue; use thiserror::Error; use super::{Eval, EvaluationContext, ResolverContextLike, IO}; +use crate::auth; use crate::blueprint::DynamicValue; -use crate::javascript::Runtime; use crate::json::JsonLike; use crate::lambda::cache::Cache; use crate::serde_value_ext::ValueExt; -use crate::{auth, WorkerIO}; #[derive(Clone, Debug)] pub enum Expression { @@ -23,7 +22,6 @@ pub enum Expression { Cache(Cache), Path(Box, Vec), Protect(Box), - Js(Arc), } impl Display for Expression { @@ -35,7 +33,6 @@ impl Display for Expression { Expression::Cache(_) => write!(f, "Cache"), Expression::Path(_, _) => write!(f, "Input"), Expression::Protect(expr) => write!(f, "Protected({expr})"), - Expression::Js(_) => write!(f, "Js"), } } } @@ -193,10 +190,6 @@ impl Eval for Expression { } Expression::IO(operation) => operation.eval(ctx).await, Expression::Cache(cached) => cached.eval(ctx).await, - Expression::Js(js) => { - let val = js.call(ctx.value().cloned()).await?; - Ok(val.unwrap_or(async_graphql::Value::Null)) - } } }) } diff --git a/src/lambda/io.rs b/src/lambda/io.rs index 50e92c00c9..1b1c91a34d 100644 --- a/src/lambda/io.rs +++ b/src/lambda/io.rs @@ -1,4 +1,5 @@ use core::future::Future; +use std::hash::{Hash, Hasher}; use std::pin::Pin; use std::sync::Arc; @@ -39,6 +40,9 @@ pub enum IO { group_by: Option, dl_id: Option, }, + Js { + name: String, + }, } #[derive(Clone, Copy, Debug)] @@ -133,6 +137,17 @@ impl IO { Ok(res.body) } + IO::Js { name: method } => { + let value = ctx + .request_ctx + .runtime + .resolver_worker + .call(method.clone(), ctx.value().cloned()) + .await + .map_err(EvaluationError::from)?; + + Ok(value.unwrap_or_default()) + } } }) } @@ -144,6 +159,12 @@ impl<'a, Ctx: ResolverContextLike<'a> + Sync + Send> CacheKey req_template.cache_key(ctx), IO::Grpc { req_template, .. } => req_template.cache_key(ctx), IO::GraphQL { req_template, .. } => req_template.cache_key(ctx), + IO::Js { name: method } => { + // TODO + let mut hasher = std::hash::DefaultHasher::new(); + method.hash(&mut hasher); + hasher.finish() + } } } } diff --git a/src/lambda/modify.rs b/src/lambda/modify.rs index 2ce6b96329..afd59a3443 100644 --- a/src/lambda/modify.rs +++ b/src/lambda/modify.rs @@ -37,7 +37,6 @@ impl Expression { }) } }, - Expression::Js(_) => expr, Expression::Dynamic(_) => expr, Expression::IO(_) => expr, Expression::Cache(Cache { expr, max_age }) => { diff --git a/src/lib.rs b/src/lib.rs index 7eab82fd9d..7b2ad931f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ pub type EntityCache = dyn Cache; #[async_trait::async_trait] pub trait WorkerIO: Send + Sync + 'static { /// Calls a global JS function - async fn call(&self, input: In) -> anyhow::Result>; + async fn call(&self, name: String, input: In) -> anyhow::Result>; } pub fn is_default(val: &T) -> bool { diff --git a/src/runtime.rs b/src/runtime.rs index 1a751fa8bf..d40a90880f 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -2,8 +2,9 @@ use std::sync::Arc; use async_graphql_value::ConstValue; +use crate::javascript::{Command, Event}; use crate::schema_extension::SchemaExtension; -use crate::{Cache, EnvIO, FileIO, HttpIO}; +use crate::{Cache, EnvIO, FileIO, HttpIO, WorkerIO}; /// The TargetRuntime struct unifies the available runtime-specific /// IO implementations. This is used to reduce piping IO structs all @@ -26,6 +27,10 @@ pub struct TargetRuntime { /// A list of extensions that can be used to extend the runtime's /// functionality or integrate additional features. pub extensions: Arc>, + /// Worker middleware for handling HTTP requests. + pub http_worker: Arc>, + /// Worker middleware for resolving data. + pub resolver_worker: Arc, ConstValue>>, } impl TargetRuntime { @@ -42,6 +47,8 @@ pub mod test { use std::time::Duration; use anyhow::{anyhow, Result}; + use async_graphql_value::ConstValue; + use async_trait::async_trait; use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions, MokaManager}; use hyper::body::Bytes; use reqwest::Client; @@ -51,8 +58,9 @@ pub mod test { use crate::blueprint::Upstream; use crate::cache::InMemoryCache; use crate::http::Response; + use crate::javascript::{Command, Event}; use crate::runtime::TargetRuntime; - use crate::{blueprint, javascript, EnvIO, FileIO, HttpIO}; + use crate::{blueprint, javascript, EnvIO, FileIO, HttpIO, WorkerIO}; #[derive(Clone)] struct TestHttp { @@ -163,6 +171,22 @@ pub mod test { } } + struct TestWorker {} + + #[async_trait] + impl WorkerIO, ConstValue> for TestWorker { + async fn call(&self, _: String, _: Option) -> Result> { + todo!() + } + } + + #[async_trait] + impl WorkerIO for TestWorker { + async fn call(&self, _: String, _: Event) -> Result> { + todo!() + } + } + pub fn init(script: Option) -> TargetRuntime { let http = if let Some(script) = script.clone() { javascript::init_http(TestHttp::init(&Default::default()), script) @@ -189,6 +213,8 @@ pub mod test { file: Arc::new(file), cache: Arc::new(InMemoryCache::new()), extensions: Arc::new(vec![]), + http_worker: Arc::new(TestWorker {}), + resolver_worker: Arc::new(TestWorker {}), } } } diff --git a/tailcall-aws-lambda/Cargo.toml b/tailcall-aws-lambda/Cargo.toml index 811139f933..0ae8b702f8 100644 --- a/tailcall-aws-lambda/Cargo.toml +++ b/tailcall-aws-lambda/Cargo.toml @@ -24,6 +24,7 @@ dotenvy = "0.15.7" anyhow = "1.0.82" async-trait = "0.1.80" +async-graphql-value = "7.0.3" hyper = { version = "0.14.28", default-features = false } reqwest = { version = "0.11", default-features = false } tailcall = { path = "..", default-features = false } diff --git a/tailcall-aws-lambda/src/main.rs b/tailcall-aws-lambda/src/main.rs index 69cb3932b2..452acdd42a 100644 --- a/tailcall-aws-lambda/src/main.rs +++ b/tailcall-aws-lambda/src/main.rs @@ -12,6 +12,7 @@ use tailcall::tracing::get_log_level; mod http; mod runtime; +mod worker_io; #[tokio::main] async fn main() -> Result<(), Error> { diff --git a/tailcall-aws-lambda/src/runtime.rs b/tailcall-aws-lambda/src/runtime.rs index 368bab98d5..be126e5573 100644 --- a/tailcall-aws-lambda/src/runtime.rs +++ b/tailcall-aws-lambda/src/runtime.rs @@ -8,6 +8,7 @@ use tailcall::{EntityCache, EnvIO, FileIO}; use tokio::io::AsyncReadExt; use crate::http::init_http; +use crate::worker_io; #[derive(Clone, Copy)] pub struct LambdaEnv; @@ -60,5 +61,7 @@ pub fn init_runtime() -> TargetRuntime { env: init_env(), cache: init_cache(), extensions: Arc::new(vec![]), + http_worker: Arc::new(worker_io::JsRuntime::init()), + resolver_worker: Arc::new(worker_io::JsRuntime::init()), } } diff --git a/tailcall-aws-lambda/src/worker_io.rs b/tailcall-aws-lambda/src/worker_io.rs new file mode 100644 index 0000000000..f3af02710d --- /dev/null +++ b/tailcall-aws-lambda/src/worker_io.rs @@ -0,0 +1,25 @@ +use async_graphql_value::ConstValue; +use tailcall::javascript::{Command, Event}; +use tailcall::WorkerIO; + +pub struct JsRuntime {} + +impl JsRuntime { + pub fn init() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl WorkerIO for JsRuntime { + async fn call(&self, _: String, _: Event) -> anyhow::Result> { + todo!() + } +} + +#[async_trait::async_trait] +impl WorkerIO, ConstValue> for JsRuntime { + async fn call(&self, _: String, _: Option) -> anyhow::Result> { + todo!() + } +} diff --git a/tailcall-cloudflare/src/lib.rs b/tailcall-cloudflare/src/lib.rs index a1dc942447..73dab6d4f0 100644 --- a/tailcall-cloudflare/src/lib.rs +++ b/tailcall-cloudflare/src/lib.rs @@ -8,6 +8,7 @@ mod file; pub mod handle; mod http; mod runtime; +mod worker_io; #[worker::event(fetch)] async fn fetch( diff --git a/tailcall-cloudflare/src/runtime.rs b/tailcall-cloudflare/src/runtime.rs index 6bc38cf50e..2a69cd315b 100644 --- a/tailcall-cloudflare/src/runtime.rs +++ b/tailcall-cloudflare/src/runtime.rs @@ -6,7 +6,7 @@ use async_graphql_value::ConstValue; use tailcall::runtime::TargetRuntime; use tailcall::{EnvIO, FileIO, HttpIO}; -use crate::{cache, env, file, http}; +use crate::{cache, env, file, http, worker_io}; fn init_env(env: Rc) -> Arc { Arc::new(env::CloudflareEnv::init(env)) @@ -38,5 +38,7 @@ pub fn init(env: Rc) -> anyhow::Result { file: init_file(env.clone(), &bucket_id)?, cache: init_cache(env), extensions: Arc::new(vec![]), + http_worker: Arc::new(worker_io::JsRuntime::init()), + resolver_worker: Arc::new(worker_io::JsRuntime::init()), }) } diff --git a/tailcall-cloudflare/src/worker_io.rs b/tailcall-cloudflare/src/worker_io.rs new file mode 100644 index 0000000000..f3af02710d --- /dev/null +++ b/tailcall-cloudflare/src/worker_io.rs @@ -0,0 +1,25 @@ +use async_graphql_value::ConstValue; +use tailcall::javascript::{Command, Event}; +use tailcall::WorkerIO; + +pub struct JsRuntime {} + +impl JsRuntime { + pub fn init() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl WorkerIO for JsRuntime { + async fn call(&self, _: String, _: Event) -> anyhow::Result> { + todo!() + } +} + +#[async_trait::async_trait] +impl WorkerIO, ConstValue> for JsRuntime { + async fn call(&self, _: String, _: Option) -> anyhow::Result> { + todo!() + } +} diff --git a/tests/core/mod.rs b/tests/core/mod.rs index 87866778a3..ded733d55e 100644 --- a/tests/core/mod.rs +++ b/tests/core/mod.rs @@ -5,3 +5,4 @@ mod model; mod parse; mod runtime; pub mod spec; +pub mod worker_io; diff --git a/tests/core/parse.rs b/tests/core/parse.rs index 2076da9dc0..df47551a78 100644 --- a/tests/core/parse.rs +++ b/tests/core/parse.rs @@ -21,6 +21,7 @@ use super::file::File; use super::http::Http; use super::model::*; use super::runtime::ExecutionSpec; +use crate::core::worker_io; struct Env { env: HashMap, @@ -289,6 +290,8 @@ impl ExecutionSpec { env: Arc::new(Env::init(env)), cache: Arc::new(InMemoryCache::new()), extensions: Arc::new(vec![]), + http_worker: Arc::new(worker_io::JsRuntime::init()), + resolver_worker: Arc::new(worker_io::JsRuntime::init()), }; let endpoints = config diff --git a/tests/core/runtime.rs b/tests/core/runtime.rs index f9e6125b00..531bc22ee0 100644 --- a/tests/core/runtime.rs +++ b/tests/core/runtime.rs @@ -15,6 +15,7 @@ use super::env::Env; use super::file::TestFileIO; use super::http::Http; use super::model::*; +use crate::core::worker_io::JsRuntime; #[derive(Clone, Setters)] pub struct ExecutionSpec { @@ -86,5 +87,7 @@ pub fn create_runtime( file: Arc::new(file), cache: Arc::new(InMemoryCache::new()), extensions: Arc::new(vec![]), + http_worker: Arc::new(JsRuntime::init()), + resolver_worker: Arc::new(JsRuntime::init()), } } diff --git a/tests/core/worker_io.rs b/tests/core/worker_io.rs new file mode 100644 index 0000000000..f3af02710d --- /dev/null +++ b/tests/core/worker_io.rs @@ -0,0 +1,25 @@ +use async_graphql_value::ConstValue; +use tailcall::javascript::{Command, Event}; +use tailcall::WorkerIO; + +pub struct JsRuntime {} + +impl JsRuntime { + pub fn init() -> Self { + Self {} + } +} + +#[async_trait::async_trait] +impl WorkerIO for JsRuntime { + async fn call(&self, _: String, _: Event) -> anyhow::Result> { + todo!() + } +} + +#[async_trait::async_trait] +impl WorkerIO, ConstValue> for JsRuntime { + async fn call(&self, _: String, _: Option) -> anyhow::Result> { + todo!() + } +} diff --git a/tests/server_spec.rs b/tests/server_spec.rs index 2b3ca62614..d5761a7b7d 100644 --- a/tests/server_spec.rs +++ b/tests/server_spec.rs @@ -8,19 +8,47 @@ pub mod test { use std::time::Duration; use anyhow::{anyhow, Result}; + use async_graphql_value::ConstValue; use http_cache_reqwest::{Cache, CacheMode, HttpCache, HttpCacheOptions, MokaManager}; use hyper::body::Bytes; use reqwest::Client; use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use tailcall::cache::InMemoryCache; use tailcall::http::Response; - use tailcall::javascript; + use tailcall::javascript::{Command, Event}; use tailcall::runtime::TargetRuntime; + use tailcall::{javascript, WorkerIO}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use crate::blueprint::Upstream; use crate::{blueprint, EnvIO, FileIO, HttpIO}; + pub struct JsRuntime {} + + impl JsRuntime { + pub fn init() -> Self { + Self {} + } + } + + #[async_trait::async_trait] + impl WorkerIO for JsRuntime { + async fn call(&self, _: String, _: Event) -> anyhow::Result> { + todo!() + } + } + + #[async_trait::async_trait] + impl WorkerIO, ConstValue> for JsRuntime { + async fn call( + &self, + _: String, + _: Option, + ) -> anyhow::Result> { + todo!() + } + } + #[derive(Clone)] struct TestHttp { client: ClientWithMiddleware, @@ -156,6 +184,8 @@ pub mod test { file: Arc::new(file), cache: Arc::new(InMemoryCache::new()), extensions: Arc::new(vec![]), + http_worker: Arc::new(JsRuntime::init()), + resolver_worker: Arc::new(JsRuntime::init()), } } } From 67fcc23f21e87245141f6e55ebdfce208664c7ed Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Thu, 9 May 2024 12:34:19 +0530 Subject: [PATCH 05/25] merge main --- Cargo.lock | 8 ++++---- benches/data_loader_bench.rs | 7 +++++-- .../impl_path_string_for_evaluation_context.rs | 6 +++--- src/cli/runtime/mod.rs | 10 +++++----- src/core/blueprint/operators/js.rs | 17 +++++++++-------- .../javascript/enable_js/js_request.rs | 2 +- .../javascript/enable_js/js_response.rs | 2 +- src/{ => core}/javascript/enable_js/mod.rs | 0 .../javascript/enable_js/request_filter.rs | 5 ++--- src/{ => core}/javascript/enable_js/runtime.rs | 2 +- src/{ => core}/javascript/mod.rs | 5 ++--- src/{ => core}/javascript/runtime_no_js.rs | 0 src/{ => core}/javascript/shim/console.js | 0 src/core/mod.rs | 1 + src/core/runtime.rs | 5 +++-- src/lib.rs | 2 +- tailcall-aws-lambda/src/worker_io.rs | 2 +- tailcall-cloudflare/src/worker_io.rs | 2 +- tests/core/parse.rs | 1 + tests/core/runtime.rs | 1 + tests/core/worker_io.rs | 2 +- tests/server_spec.rs | 2 ++ 22 files changed, 45 insertions(+), 37 deletions(-) rename src/{ => core}/javascript/enable_js/js_request.rs (99%) rename src/{ => core}/javascript/enable_js/js_response.rs (99%) rename src/{ => core}/javascript/enable_js/mod.rs (100%) rename src/{ => core}/javascript/enable_js/request_filter.rs (97%) rename src/{ => core}/javascript/enable_js/runtime.rs (99%) rename src/{ => core}/javascript/mod.rs (83%) rename src/{ => core}/javascript/runtime_no_js.rs (100%) rename src/{ => core}/javascript/shim/console.js (100%) diff --git a/Cargo.lock b/Cargo.lock index 620e88817d..b93f5c779a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6154,8 +6154,8 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -6165,8 +6165,8 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] diff --git a/benches/data_loader_bench.rs b/benches/data_loader_bench.rs index 71a92c233e..a63548319f 100644 --- a/benches/data_loader_bench.rs +++ b/benches/data_loader_bench.rs @@ -9,8 +9,11 @@ use async_graphql_value::ConstValue; use criterion::Criterion; use hyper::body::Bytes; use reqwest::Request; -use tailcall::{Batch, DataLoaderRequest, EnvIO, FileIO, HttpDataLoader, HttpIO, Response, TargetRuntime, WorkerIO}; -use tailcall::javascript::{Command, Event}; +use tailcall::core::javascript::{Command, Event}; +use tailcall::{ + Batch, DataLoaderRequest, EnvIO, FileIO, HttpDataLoader, HttpIO, Response, TargetRuntime, + WorkerIO, +}; #[derive(Clone)] struct MockHttpClient { diff --git a/benches/impl_path_string_for_evaluation_context.rs b/benches/impl_path_string_for_evaluation_context.rs index cb51cbdcd9..3e819782fe 100644 --- a/benches/impl_path_string_for_evaluation_context.rs +++ b/benches/impl_path_string_for_evaluation_context.rs @@ -16,10 +16,10 @@ use indexmap::IndexMap; use once_cell::sync::Lazy; use reqwest::{Client, Request}; use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; -use tailcall::javascript::{Command, Event}; +use tailcall::core::javascript::{Command, Event}; use tailcall::{ - WorkerIO, EnvIO, EvaluationContext, FileIO, HttpIO, InMemoryCache, PathString, RequestContext, - ResolverContextLike, Response, Server, TargetRuntime, Upstream, + EnvIO, EvaluationContext, FileIO, HttpIO, InMemoryCache, PathString, RequestContext, + ResolverContextLike, Response, Server, TargetRuntime, Upstream, WorkerIO, }; pub struct JsRuntime {} diff --git a/src/cli/runtime/mod.rs b/src/cli/runtime/mod.rs index 8e5ecc5ff6..09eec2109d 100644 --- a/src/cli/runtime/mod.rs +++ b/src/cli/runtime/mod.rs @@ -7,9 +7,9 @@ use std::sync::Arc; use crate::core::blueprint::Blueprint; use crate::core::cache::InMemoryCache; -use crate::javascript::{Command, Event}; +use crate::core::javascript::{Command, Event}; use crate::core::runtime::TargetRuntime; -use crate::core::{blueprint, EnvIO, FileIO, HttpIO, WorkerIO}; +use crate::core::{blueprint, javascript, EnvIO, FileIO, HttpIO, WorkerIO}; // Provides access to env in native rust environment fn init_env() -> Arc { @@ -24,7 +24,7 @@ fn init_file() -> Arc { fn init_hook_http(http: Arc, script: Option) -> Arc { #[cfg(feature = "js")] if let Some(script) = script { - return crate::javascript::init_http(http, script); + return javascript::init_http(http, script); } #[cfg(not(feature = "js"))] @@ -35,13 +35,13 @@ fn init_hook_http(http: Arc, script: Option) -> } fn init_http_worker_io(script: Option) -> Arc> { - crate::javascript::init_rt(script) + javascript::init_rt(script) } fn init_resolver_worker_io( script: Option, ) -> Arc, async_graphql::Value>> { - crate::javascript::init_rt(script) + javascript::init_rt(script) } // Provides access to http in native rust environment diff --git a/src/core/blueprint/operators/js.rs b/src/core/blueprint/operators/js.rs index f155bf8d0d..624c6dad4d 100644 --- a/src/core/blueprint/operators/js.rs +++ b/src/core/blueprint/operators/js.rs @@ -1,9 +1,10 @@ -use crate::blueprint::*; -use crate::config; -use crate::config::Field; -use crate::lambda::Expression; -use crate::try_fold::TryFold; -use crate::valid::{Valid, Validator}; +use crate::core::blueprint::FieldDefinition; +use crate::core::config; +use crate::core::config::Field; +use crate::core::lambda::IO; +use crate::core::try_fold::TryFold; +use crate::core::valid::Valid; +use crate::{ConfigModule, Expression, ValidationError, Validator}; pub struct CompileJs<'a> { pub name: &'a str, @@ -36,9 +37,9 @@ fn js_enabled(inputs: CompileJs) -> Valid { ctx.eval::<(), &str>(script)?; Ok::<_, anyhow::Error>(()) }) - .map_err(|e| crate::valid::ValidationError::new(e.to_string())), + .map_err(|e| ValidationError::new(e.to_string())), ) - .map(|_| Expression::IO(crate::lambda::IO::Js { name: name.to_string() })) + .map(|_| Expression::IO(IO::Js { name: name.to_string() })) }, ) } diff --git a/src/javascript/enable_js/js_request.rs b/src/core/javascript/enable_js/js_request.rs similarity index 99% rename from src/javascript/enable_js/js_request.rs rename to src/core/javascript/enable_js/js_request.rs index 537ab5598c..3d4bc7434c 100644 --- a/src/javascript/enable_js/js_request.rs +++ b/src/core/javascript/enable_js/js_request.rs @@ -9,7 +9,7 @@ use rquickjs::{FromJs, IntoJs}; use serde::{Deserialize, Serialize}; use crate::core::is_default; -use crate::javascript::JsRequest; +use crate::core::javascript::JsRequest; impl JsRequest { fn uri(&self) -> Uri { diff --git a/src/javascript/enable_js/js_response.rs b/src/core/javascript/enable_js/js_response.rs similarity index 99% rename from src/javascript/enable_js/js_response.rs rename to src/core/javascript/enable_js/js_response.rs index b712d58f77..810791d844 100644 --- a/src/javascript/enable_js/js_response.rs +++ b/src/core/javascript/enable_js/js_response.rs @@ -5,7 +5,7 @@ use rquickjs::{FromJs, IntoJs}; use super::create_header_map; use crate::core::http::Response; -use crate::javascript::JsResponse; +use crate::core::javascript::JsResponse; impl JsResponse { pub fn status(&self) -> u16 { diff --git a/src/javascript/enable_js/mod.rs b/src/core/javascript/enable_js/mod.rs similarity index 100% rename from src/javascript/enable_js/mod.rs rename to src/core/javascript/enable_js/mod.rs diff --git a/src/javascript/enable_js/request_filter.rs b/src/core/javascript/enable_js/request_filter.rs similarity index 97% rename from src/javascript/enable_js/request_filter.rs rename to src/core/javascript/enable_js/request_filter.rs index 9f7a0c06fd..d983eb9bbc 100644 --- a/src/javascript/enable_js/request_filter.rs +++ b/src/core/javascript/enable_js/request_filter.rs @@ -3,8 +3,8 @@ use std::sync::Arc; use hyper::body::Bytes; use rquickjs::FromJs; -use super::{JsRequest, JsResponse}; use crate::core::http::Response; +use crate::core::javascript::{Command, Event, JsRequest, JsResponse}; use crate::core::{HttpIO, WorkerIO}; impl<'js> FromJs<'js> for Command { @@ -93,9 +93,8 @@ mod tests { use hyper::body::Bytes; use rquickjs::{Context, FromJs, IntoJs, Object, Runtime, String as JsString}; - use crate::cli::javascript::request_filter::Command; - use crate::cli::javascript::{JsRequest, JsResponse}; use crate::core::http::Response; + use crate::core::javascript::{Command, JsRequest, JsResponse}; #[test] fn test_command_from_invalid_object() { diff --git a/src/javascript/enable_js/runtime.rs b/src/core/javascript/enable_js/runtime.rs similarity index 99% rename from src/javascript/enable_js/runtime.rs rename to src/core/javascript/enable_js/runtime.rs index c8731f886f..9e052f51e2 100644 --- a/src/javascript/enable_js/runtime.rs +++ b/src/core/javascript/enable_js/runtime.rs @@ -5,7 +5,7 @@ use std::thread; use async_graphql_value::ConstValue; use rquickjs::{Context, Ctx, FromJs, Function, IntoJs, Value}; -use crate::javascript::{Command, Event, JsRequest}; +use crate::core::javascript::{Command, Event, JsRequest}; use crate::core::{blueprint, WorkerIO}; struct LocalRuntime(Context); diff --git a/src/javascript/mod.rs b/src/core/javascript/mod.rs similarity index 83% rename from src/javascript/mod.rs rename to src/core/javascript/mod.rs index 324500270a..8b327735d8 100644 --- a/src/javascript/mod.rs +++ b/src/core/javascript/mod.rs @@ -27,10 +27,9 @@ pub enum Command { Response(JsResponse), } -use crate::blueprint::Script; -use crate::http::Response; +use crate::{Response, Script}; -pub fn init_rt(script: Option) -> Arc { +pub fn init_rt(script: Option