Skip to content

Commit

Permalink
feat(showcase): adapt to TargetRuntime
Browse files Browse the repository at this point in the history
  • Loading branch information
mogery committed Feb 1, 2024
1 parent 27c9c36 commit 6bd71da
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 103 deletions.
28 changes: 5 additions & 23 deletions cloudflare/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,12 @@ use std::collections::HashMap;
use std::rc::Rc;
use std::sync::{Arc, RwLock};

use anyhow::anyhow;
use hyper::{Body, Method, Request, Response};
use lazy_static::lazy_static;
use tailcall::async_graphql_hyper::GraphQLRequest;
use tailcall::http::{
graphiql, handle_request, showcase_get_app_ctx, AppContext, ShowcaseResources,
};
use tailcall::EnvIO;
use tailcall::http::{graphiql, handle_request, showcase, AppContext};

use crate::env::CloudflareEnv;
use crate::http::{to_request, to_response, CloudflareHttp};
use crate::http::{to_request, to_response};
use crate::init_runtime;

lazy_static! {
Expand Down Expand Up @@ -75,23 +70,10 @@ async fn get_app_ctx(
}
}

// Create new context
let env_io = Arc::new(CloudflareEnv::init(env.clone()));
let bucket_id = env_io
.get("BUCKET")
.ok_or(anyhow!("CONFIG var is not set"))?;
log::debug!("R2 Bucket ID: {}", bucket_id);

let resources = ShowcaseResources {
http: init_http(),
file: Some(init_file(env.clone(), bucket_id)?),
env: Some(env_io),
cache: init_cache(env),
};

match showcase_get_app_ctx::<GraphQLRequest>(req, resources).await? {
let runtime = init_runtime(env)?;
match showcase::create_app_ctx::<GraphQLRequest>(req, runtime, true).await? {
Ok(app_ctx) => {
let app_ctx = Arc::new(app_ctx);
let app_ctx: Arc<AppContext> = Arc::new(app_ctx);
if let Some(file_path) = file_path {
*APP_CTX.write().unwrap() = Some((file_path, app_ctx.clone()));
}
Expand Down
3 changes: 3 additions & 0 deletions cloudflare/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ pub fn init_runtime(env: Rc<worker::Env>) -> anyhow::Result<TargetRuntime> {
let bucket_id = env_io
.get("BUCKET")
.ok_or(anyhow!("BUCKET var is not set"))?;

log::debug!("R2 Bucket ID: {}", bucket_id);

Ok(TargetRuntime {
http: http.clone(),
http2_only: http.clone(),
Expand Down
10 changes: 2 additions & 8 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,13 @@ pub fn init_hook_http(http: impl HttpIO, script: Option<blueprint::Script>) -> A
}

// Provides access to http in native rust environment
pub fn init_http(
upstream: &Upstream,
script: Option<blueprint::Script>,
) -> Arc<dyn HttpIO + Send + Sync> {
pub fn init_http(upstream: &Upstream, script: Option<blueprint::Script>) -> Arc<dyn HttpIO> {
let http_io = http::NativeHttp::init(upstream);
init_hook_http(http_io, script)
}

// Provides access to http in native rust environment
pub fn init_http2_only(
upstream: &Upstream,
script: Option<blueprint::Script>,
) -> Arc<dyn HttpIO + Send + Sync> {
pub fn init_http2_only(upstream: &Upstream, script: Option<blueprint::Script>) -> Arc<dyn HttpIO> {
let http_io = http::NativeHttp::init(&upstream.clone().http2_only(true));
init_hook_http(http_io, script)
}
Expand Down
2 changes: 1 addition & 1 deletion src/config/reader.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::{HashMap, VecDeque};

use anyhow::{anyhow, Context};
use anyhow::Context;
use futures_util::future::join_all;
use futures_util::TryFutureExt;
use prost_reflect::prost_types::{FileDescriptorProto, FileDescriptorSet};
Expand Down
19 changes: 5 additions & 14 deletions src/http/request_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use hyper::{Body, HeaderMap, Request, Response, StatusCode};
use serde::de::DeserializeOwned;

use super::request_context::RequestContext;
use super::showcase::ShowcaseResources;
use super::{showcase, AppContext};
use crate::async_graphql_hyper::{GraphQLRequestLike, GraphQLResponse};

Expand Down Expand Up @@ -121,19 +120,11 @@ pub async fn handle_request<T: DeserializeOwned + GraphQLRequestLike>(
if app_ctx.blueprint.server.enable_showcase
&& req.uri().path() == "/showcase/graphql" =>
{
let resources = ShowcaseResources {
http: app_ctx.universal_http_client.clone(),
env: None, // Disallow access to environment variables
file: None, // Disallow local file reading

// TODO: Generically accessible way to create new clean cache
cache: app_ctx.cache.clone(),
};

let app_ctx = match showcase::create_app_ctx::<T>(&req, resources).await? {
Ok(app_ctx) => app_ctx,
Err(res) => return Ok(res),
};
let app_ctx =
match showcase::create_app_ctx::<T>(&req, app_ctx.runtime.clone(), false).await? {
Ok(app_ctx) => app_ctx,
Err(res) => return Ok(res),
};

graphql_request::<T>(req, &app_ctx).await
}
Expand Down
86 changes: 32 additions & 54 deletions src/http/showcase.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::sync::Arc;

use anyhow::Result;
use async_graphql::ServerError;
use hyper::{Body, Request, Response};
Expand All @@ -10,32 +8,16 @@ use super::AppContext;
use crate::async_graphql_hyper::{GraphQLRequestLike, GraphQLResponse};
use crate::blueprint::Blueprint;
use crate::config::reader::ConfigReader;
use crate::{EntityCache, EnvIO, FileIO, HttpIO};

struct DummyEnvIO;

impl EnvIO for DummyEnvIO {
fn get(&self, _key: &str) -> Option<String> {
None
}
}

pub struct ShowcaseResources {
pub http: Arc<dyn HttpIO + Send + Sync>,
pub env: Option<Arc<dyn EnvIO>>,
pub file: Option<Arc<dyn FileIO + Send + Sync>>,
pub cache: Arc<EntityCache>,
}
use crate::target_runtime::TargetRuntime;

pub async fn create_app_ctx<T: DeserializeOwned + GraphQLRequestLike>(
req: &Request<Body>,
resources: ShowcaseResources,
runtime: TargetRuntime,
enable_fs: bool,
) -> Result<Result<AppContext, Response<Body>>> {
let url = Url::parse(&req.uri().to_string())?;
let mut query = url.query_pairs();

let http = resources.http;

let config_url = if let Some(pair) = query.find(|x| x.0 == "config") {
pair.1.to_string()
} else {
Expand All @@ -45,7 +27,14 @@ pub async fn create_app_ctx<T: DeserializeOwned + GraphQLRequestLike>(
return Ok(Err(GraphQLResponse::from(response).to_response()?));
};

let reader = ConfigReader::init(resources.file, http.clone());
if !enable_fs && Url::parse(&config_url).is_err() {
let mut response = async_graphql::Response::default();
let server_error = ServerError::new("Invalid Config URL specified", None);
response.errors = vec![server_error];
return Ok(Err(GraphQLResponse::from(response).to_response()?));
}

let reader = ConfigReader::init(runtime.clone());
let config = match reader.read(config_url).await {
Ok(config) => config,
Err(e) => {
Expand All @@ -66,15 +55,7 @@ pub async fn create_app_ctx<T: DeserializeOwned + GraphQLRequestLike>(
}
};

let env = resources.env.unwrap_or_else(|| Arc::new(DummyEnvIO));

Ok(Ok(AppContext::new(
blueprint,
http.clone(),
http,
env,
resources.cache,
)))
Ok(Ok(AppContext::new(blueprint, runtime)))
}

#[cfg(test)]
Expand All @@ -85,18 +66,10 @@ mod tests {
use serde_json::json;

use crate::async_graphql_hyper::GraphQLRequest;
use crate::cli::{init_env, init_file, init_http, init_in_memory_cache};
use crate::cli::init_runtime;
use crate::config::Upstream;
use crate::http::showcase::DummyEnvIO;
use crate::http::{handle_request, showcase_get_app_ctx, ShowcaseResources};
use crate::EnvIO as _;

#[test]
fn dummy_env_works() {
let env = DummyEnvIO;

assert!(env.get("PATH").is_none());
}
use crate::http::handle_request;
use crate::http::showcase::create_app_ctx;

#[tokio::test]
async fn works_with_file() {
Expand All @@ -108,23 +81,28 @@ mod tests {
}).to_string()))
.unwrap();

let app = showcase_get_app_ctx::<GraphQLRequest>(
&req,
ShowcaseResources {
http: init_http(&Upstream::default(), None),
env: Some(init_env()),
file: Some(init_file()),
cache: Arc::new(init_in_memory_cache()),
},
)
.await
.unwrap()
.unwrap();
let runtime = init_runtime(&Upstream::default(), None);
let app = create_app_ctx::<GraphQLRequest>(&req, runtime, true)
.await
.unwrap()
.unwrap();

let req = Request::builder()
.method("POST")
.uri("http://upstream/graphql?config=.%2Ftests%2Fhttp%2Fconfig%2Fsimple.graphql")
.body(hyper::Body::from(
json!({
"query": "query { user { name } }"
})
.to_string(),
))
.unwrap();

let res = handle_request::<GraphQLRequest>(req, Arc::new(app))
.await
.unwrap();

println!("{:#?}", res);
assert!(res.status().is_success())
}
}
4 changes: 2 additions & 2 deletions tests/graphql_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,8 @@ async fn test_execution() -> std::io::Result<()> {
.to_result()
.unwrap();
let runtime = init_runtime(&blueprint.upstream, None);
let server_ctx = AppContext::new(blueprint, runtime);
let schema = &server_ctx.schema;
let app_ctx = AppContext::new(blueprint, runtime);
let schema = &app_ctx.schema;

for q in spec.test_queries {
let mut headers = HeaderMap::new();
Expand Down
2 changes: 1 addition & 1 deletion tests/http/showcase.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ assert:
body:
data: null
errors:
- message: "Failed to read config: ./tests/http/config/simple.graphql"
- message: "Invalid Config URL specified"
- request:
method: POST
url: http://localhost:8080/showcase/graphql?config=http%3A%2F%2Fexample.com%2Finvalid.graphql
Expand Down

1 comment on commit 6bd71da

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running 30s test @ http://localhost:8000/graphql

4 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 6.42ms 2.61ms 23.46ms 69.19%
Req/Sec 3.92k 112.45 5.80k 84.42%

468318 requests in 30.02s, 2.35GB read

Requests/sec: 15599.45

Transfer/sec: 80.07MB

Please sign in to comment.