Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(js): JS based resolvers #1867

Merged
merged 31 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f8259b7
impl WorkerIO in Rt
ssddOnTop May 7, 2024
5276715
fix wasm build
ssddOnTop May 7, 2024
fc38284
undo changes in example
ssddOnTop May 8, 2024
701052c
move Js to Expression::IO
ssddOnTop May 8, 2024
8b715cb
Merge branch 'main' into feat/js-filter
ssddOnTop May 9, 2024
67fcc23
merge main
ssddOnTop May 9, 2024
fde8775
fix tests
ssddOnTop May 9, 2024
2faf7eb
miscellaneous
ssddOnTop May 9, 2024
f5fcf0c
drop feature flags in js.rs
ssddOnTop May 9, 2024
1c04f46
drop unwanted feature flags
ssddOnTop May 9, 2024
a7d5df5
make core not public
ssddOnTop May 9, 2024
4d5f12e
drop unwanted impl of WorkerIO
ssddOnTop May 9, 2024
2757fb3
Merge branch 'main' into feat/js-filter
ssddOnTop May 9, 2024
211ac0d
Merge branch 'main' into feat/js-filter
ssddOnTop May 18, 2024
8a2ebc1
merge main
ssddOnTop May 18, 2024
4cbaf4f
lint fixes
ssddOnTop May 18, 2024
9ba2d11
refactor: rename javascript to worker
ssddOnTop May 18, 2024
e30e127
fix log
ssddOnTop May 18, 2024
80a4260
Merge branch 'main' into feat/js-filter
ssddOnTop May 22, 2024
f42396a
lock
ssddOnTop May 22, 2024
48bc45a
convert String to Cow<str>
ssddOnTop May 22, 2024
2f7168b
Merge branch 'main' into feat/js-filter
ssddOnTop May 22, 2024
5c7af2b
cow to &str
ssddOnTop May 23, 2024
a483d81
Merge branch 'main' into feat/js-filter
ssddOnTop May 23, 2024
399f43f
merge
ssddOnTop May 23, 2024
c088e92
partial
ssddOnTop May 24, 2024
10aa70b
add tests for js directive
ssddOnTop May 24, 2024
a4dd223
miscellaneous
ssddOnTop May 24, 2024
02d77a3
update tests
tusharmath May 24, 2024
40a2bf5
change sig from `&In` to `In`
ssddOnTop May 24, 2024
8f69141
drop `DefaultJsRuntime`
ssddOnTop May 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 91 additions & 185 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ lazy_static = { workspace = true }
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 = { workspace = true }
Expand Down
9 changes: 5 additions & 4 deletions benches/data_loader_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ 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::core::config::Batch;
use tailcall::core::http::{DataLoaderRequest, HttpDataLoader, Response};
use tailcall::core::runtime::TargetRuntime;
use tailcall::core::worker::DefaultJsRuntime;
use tailcall::core::{EnvIO, FileIO, HttpIO};

#[derive(Clone)]
Expand All @@ -29,7 +29,6 @@ impl HttpIO for MockHttpClient {
}

struct Env {}
#[async_trait]
impl EnvIO for Env {
fn get(&self, _: &str) -> Option<Cow<'_, str>> {
unimplemented!("Not needed for this bench")
Expand All @@ -38,7 +37,7 @@ impl EnvIO for Env {

struct File;

#[async_trait]
#[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")
Expand All @@ -50,7 +49,7 @@ impl FileIO for File {
}

struct Cache;
#[async_trait]
#[async_trait::async_trait]
impl tailcall::core::Cache for Cache {
type Key = u64;
type Value = ConstValue;
Expand Down Expand Up @@ -81,6 +80,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(DefaultJsRuntime {}),
worker: Arc::new(DefaultJsRuntime {}),
};
let loader = HttpDataLoader::new(rt, None, false);
let loader = loader.to_data_loader(Batch::default().delay(1));
Expand Down
3 changes: 3 additions & 0 deletions benches/impl_path_string_for_evaluation_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use tailcall::core::http::{RequestContext, Response};
use tailcall::core::lambda::{EvaluationContext, ResolverContextLike};
use tailcall::core::path::PathString;
use tailcall::core::runtime::TargetRuntime;
use tailcall::core::worker::DefaultJsRuntime;
use tailcall::core::{EnvIO, FileIO, HttpIO};
use tailcall_http_cache::HttpCacheManager;

Expand Down Expand Up @@ -246,6 +247,8 @@ fn request_context() -> RequestContext {
file: Arc::new(File {}),
cache: Arc::new(InMemoryCache::new()),
extensions: Arc::new(vec![]),
http_worker: Arc::new(DefaultJsRuntime {}),
worker: Arc::new(DefaultJsRuntime {}),
};
RequestContext::new(runtime)
.server(server)
Expand Down
5 changes: 5 additions & 0 deletions examples/scripts/echo.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
function onRequest({request}) {
return {request}
}

function hello(val) {
let json = JSON.parse(val)
return JSON.stringify(json.id)
}
4 changes: 2 additions & 2 deletions generated/.tailcallrc.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ directive @http(
) on FIELD_DEFINITION

directive @js(
script: String!
name: String!
) on FIELD_DEFINITION

"""
Expand Down Expand Up @@ -667,7 +667,7 @@ enum HttpVersion {
HTTP2
}
input JS {
script: String!
name: String!
}
input KeyValue {
key: String!
Expand Down
4 changes: 2 additions & 2 deletions generated/.tailcallrc.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -748,10 +748,10 @@
"JS": {
"type": "object",
"required": [
"script"
"name"
],
"properties": {
"script": {
"name": {
"type": "string"
}
}
Expand Down
28 changes: 13 additions & 15 deletions src/cli/javascript/js_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ use rquickjs::{FromJs, IntoJs};
use serde::{Deserialize, Serialize};

use crate::core::is_default;
use crate::core::worker::WorkerRequest;

#[derive(Debug)]
pub struct JsRequest(reqwest::Request);
ssddOnTop marked this conversation as resolved.
Show resolved Hide resolved

impl JsRequest {
impl WorkerRequest {
fn uri(&self) -> Uri {
self.0.url().into()
}
Expand Down Expand Up @@ -41,24 +39,24 @@ impl JsRequest {
}
}

impl TryFrom<&reqwest::Request> for JsRequest {
impl TryFrom<&reqwest::Request> for WorkerRequest {
type Error = anyhow::Error;

fn try_from(value: &Request) -> Result<Self, Self::Error> {
let request = value
.try_clone()
.ok_or(anyhow::anyhow!("unable to clone request"))?;
Ok(JsRequest(request))
Ok(WorkerRequest(request))
}
}

impl From<JsRequest> for reqwest::Request {
fn from(val: JsRequest) -> Self {
impl From<WorkerRequest> for reqwest::Request {
fn from(val: WorkerRequest) -> Self {
val.0
}
}

impl<'js> IntoJs<'js> for JsRequest {
impl<'js> IntoJs<'js> for WorkerRequest {
fn into_js(self, ctx: &rquickjs::Ctx<'js>) -> rquickjs::Result<rquickjs::Value<'js>> {
let object = rquickjs::Object::new(ctx.clone())?;
object.set("uri", self.uri())?;
Expand All @@ -76,7 +74,7 @@ impl<'js> IntoJs<'js> for JsRequest {
}
}

impl<'js> FromJs<'js> for JsRequest {
impl<'js> FromJs<'js> for WorkerRequest {
fn from_js(_: &rquickjs::Ctx<'js>, value: rquickjs::Value<'js>) -> rquickjs::Result<Self> {
let object = value.as_object().ok_or(rquickjs::Error::FromJs {
from: value.type_name(),
Expand Down Expand Up @@ -120,7 +118,7 @@ impl<'js> FromJs<'js> for JsRequest {
if let Some(body) = body {
let _ = request.body_mut().insert(reqwest::Body::from(body));
}
Ok(JsRequest(request))
Ok(WorkerRequest(request))
}
}

Expand Down Expand Up @@ -261,7 +259,7 @@ mod tests {
let _ = reqwest_request
.body_mut()
.insert(reqwest::Body::from("Hello, World!"));
let js_request: JsRequest = (&reqwest_request).try_into().unwrap();
let js_request: WorkerRequest = (&reqwest_request).try_into().unwrap();
assert_eq!(js_request.method(), "GET");
assert_eq!(js_request.uri().to_string(), "http://example.com/");
let body_out = js_request.body();
Expand All @@ -286,7 +284,7 @@ mod tests {
HeaderValue::from_str("application/json").unwrap(),
);

let js_request: JsRequest = (&request).try_into().unwrap();
let js_request: WorkerRequest = (&request).try_into().unwrap();
let value = js_request.into_js(&ctx).unwrap();
let object = value.as_object().unwrap();

Expand Down Expand Up @@ -325,10 +323,10 @@ mod tests {
HeaderValue::from_str("application/json").unwrap(),
);

let js_request: JsRequest = (&request).try_into().unwrap();
let js_request: WorkerRequest = (&request).try_into().unwrap();
let value = js_request.into_js(&ctx).unwrap();

let js_request = JsRequest::from_js(&ctx, value).unwrap();
let js_request = WorkerRequest::from_js(&ctx, value).unwrap();

assert_eq!(js_request.uri().to_string(), "http://example.com/");
assert_eq!(js_request.method(), "GET");
Expand Down
30 changes: 14 additions & 16 deletions src/cli/javascript/js_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ use rquickjs::{FromJs, IntoJs};

use super::create_header_map;
use crate::core::http::Response;
use crate::core::worker::WorkerResponse;

#[derive(Debug)]
pub struct JsResponse(Response<String>);

impl JsResponse {
impl WorkerResponse {
pub fn status(&self) -> u16 {
self.0.status.as_u16()
}
Expand All @@ -28,7 +26,7 @@ impl JsResponse {
}
}

impl<'js> IntoJs<'js> for JsResponse {
impl<'js> IntoJs<'js> for WorkerResponse {
fn into_js(self, ctx: &rquickjs::Ctx<'js>) -> rquickjs::Result<rquickjs::Value<'js>> {
let object = rquickjs::Object::new(ctx.clone())?;
object.set("status", self.status())?;
Expand All @@ -38,7 +36,7 @@ impl<'js> IntoJs<'js> for JsResponse {
}
}

impl<'js> FromJs<'js> for JsResponse {
impl<'js> FromJs<'js> for WorkerResponse {
fn from_js(_: &rquickjs::Ctx<'js>, value: rquickjs::Value<'js>) -> rquickjs::Result<Self> {
let object = value.as_object().ok_or(rquickjs::Error::FromJs {
from: value.type_name(),
Expand All @@ -61,14 +59,14 @@ impl<'js> FromJs<'js> for JsResponse {
})?,
body: body.unwrap_or_default(),
};
Ok(JsResponse(response))
Ok(WorkerResponse(response))
}
}

impl TryFrom<JsResponse> for Response<Bytes> {
impl TryFrom<WorkerResponse> for Response<Bytes> {
type Error = anyhow::Error;

fn try_from(res: JsResponse) -> Result<Self, Self::Error> {
fn try_from(res: WorkerResponse) -> Result<Self, Self::Error> {
let res = res.0;
Ok(Response {
status: res.status,
Expand All @@ -78,12 +76,12 @@ impl TryFrom<JsResponse> for Response<Bytes> {
}
}

impl TryFrom<Response<Bytes>> for JsResponse {
impl TryFrom<Response<Bytes>> for WorkerResponse {
type Error = anyhow::Error;

fn try_from(res: Response<Bytes>) -> Result<Self, Self::Error> {
let body = String::from_utf8_lossy(res.body.as_ref()).to_string();
Ok(JsResponse(Response {
Ok(WorkerResponse(Response {
status: res.status,
headers: res.headers,
body,
Expand All @@ -102,17 +100,17 @@ mod test {
use reqwest::header::HeaderMap;
use rquickjs::{Context, FromJs, IntoJs, Runtime};

use super::JsResponse;
use super::WorkerResponse;

fn create_test_response() -> Result<JsResponse> {
fn create_test_response() -> Result<WorkerResponse> {
let mut headers = HeaderMap::new();
headers.insert("content-type", "application/json".parse().unwrap());
let response = crate::core::http::Response {
status: reqwest::StatusCode::OK,
headers,
body: Bytes::from("Hello, World!"),
};
let js_response: Result<JsResponse> = response.try_into();
let js_response: Result<WorkerResponse> = response.try_into();
js_response
}

Expand Down Expand Up @@ -156,7 +154,7 @@ mod test {
headers,
body: body.into(),
};
let js_response = JsResponse(response);
let js_response = WorkerResponse(response);

let response: Result<crate::core::http::Response<Bytes>, _> = js_response.try_into();
assert!(response.is_ok());
Expand Down Expand Up @@ -195,7 +193,7 @@ mod test {
let context = Context::base(&runtime).unwrap();
context.with(|ctx| {
let js_response = create_test_response().unwrap().into_js(&ctx).unwrap();
let response = JsResponse::from_js(&ctx, js_response).unwrap();
let response = WorkerResponse::from_js(&ctx, js_response).unwrap();

assert_eq!(response.status(), reqwest::StatusCode::OK.as_u16());
assert_eq!(response.body(), Some("Hello, World!".to_owned()));
Expand Down
9 changes: 5 additions & 4 deletions src/cli/javascript/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::collections::BTreeMap;
pub use std::sync::Arc;
use std::sync::Arc;

use hyper::header::{HeaderName, HeaderValue};

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;

Expand Down
Loading
Loading