From 6bd71da4aefe67c20d5d698dee476880e14ee58a Mon Sep 17 00:00:00 2001 From: mogery Date: Thu, 1 Feb 2024 13:43:14 +0000 Subject: [PATCH] feat(showcase): adapt to TargetRuntime --- cloudflare/src/handle.rs | 28 +++--------- cloudflare/src/lib.rs | 3 ++ src/cli/mod.rs | 10 +---- src/config/reader.rs | 2 +- src/http/request_handler.rs | 19 +++----- src/http/showcase.rs | 86 ++++++++++++++----------------------- tests/graphql_spec.rs | 4 +- tests/http/showcase.yml | 2 +- 8 files changed, 51 insertions(+), 103 deletions(-) diff --git a/cloudflare/src/handle.rs b/cloudflare/src/handle.rs index c1315f1e5e..43f9752db3 100644 --- a/cloudflare/src/handle.rs +++ b/cloudflare/src/handle.rs @@ -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! { @@ -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::(req, resources).await? { + let runtime = init_runtime(env)?; + match showcase::create_app_ctx::(req, runtime, true).await? { Ok(app_ctx) => { - let app_ctx = Arc::new(app_ctx); + let app_ctx: Arc = Arc::new(app_ctx); if let Some(file_path) = file_path { *APP_CTX.write().unwrap() = Some((file_path, app_ctx.clone())); } diff --git a/cloudflare/src/lib.rs b/cloudflare/src/lib.rs index 37300b1f27..47eb9ae9f2 100644 --- a/cloudflare/src/lib.rs +++ b/cloudflare/src/lib.rs @@ -38,6 +38,9 @@ pub fn init_runtime(env: Rc) -> anyhow::Result { 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(), diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 8c9f0a6bb2..629fd8a241 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -48,19 +48,13 @@ pub fn init_hook_http(http: impl HttpIO, script: Option) -> A } // Provides access to http in native rust environment -pub fn init_http( - upstream: &Upstream, - script: Option, -) -> Arc { +pub fn init_http(upstream: &Upstream, script: Option) -> Arc { 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, -) -> Arc { +pub fn init_http2_only(upstream: &Upstream, script: Option) -> Arc { let http_io = http::NativeHttp::init(&upstream.clone().http2_only(true)); init_hook_http(http_io, script) } diff --git a/src/config/reader.rs b/src/config/reader.rs index 8a50a06ec0..825fa53c1e 100644 --- a/src/config/reader.rs +++ b/src/config/reader.rs @@ -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}; diff --git a/src/http/request_handler.rs b/src/http/request_handler.rs index 83875982b5..952c375ae2 100644 --- a/src/http/request_handler.rs +++ b/src/http/request_handler.rs @@ -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}; @@ -121,19 +120,11 @@ pub async fn handle_request( 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::(&req, resources).await? { - Ok(app_ctx) => app_ctx, - Err(res) => return Ok(res), - }; + let app_ctx = + match showcase::create_app_ctx::(&req, app_ctx.runtime.clone(), false).await? { + Ok(app_ctx) => app_ctx, + Err(res) => return Ok(res), + }; graphql_request::(req, &app_ctx).await } diff --git a/src/http/showcase.rs b/src/http/showcase.rs index bed833a296..611c40ac91 100644 --- a/src/http/showcase.rs +++ b/src/http/showcase.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use anyhow::Result; use async_graphql::ServerError; use hyper::{Body, Request, Response}; @@ -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 { - None - } -} - -pub struct ShowcaseResources { - pub http: Arc, - pub env: Option>, - pub file: Option>, - pub cache: Arc, -} +use crate::target_runtime::TargetRuntime; pub async fn create_app_ctx( req: &Request, - resources: ShowcaseResources, + runtime: TargetRuntime, + enable_fs: bool, ) -> Result>> { 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 { @@ -45,7 +27,14 @@ pub async fn create_app_ctx( 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) => { @@ -66,15 +55,7 @@ pub async fn create_app_ctx( } }; - 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)] @@ -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() { @@ -108,23 +81,28 @@ mod tests { }).to_string())) .unwrap(); - let app = showcase_get_app_ctx::( - &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::(&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::(req, Arc::new(app)) .await .unwrap(); println!("{:#?}", res); + assert!(res.status().is_success()) } } diff --git a/tests/graphql_spec.rs b/tests/graphql_spec.rs index 1188884adf..5b906e1399 100644 --- a/tests/graphql_spec.rs +++ b/tests/graphql_spec.rs @@ -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(); diff --git a/tests/http/showcase.yml b/tests/http/showcase.yml index 92a122cc9c..5ca8bd58e5 100644 --- a/tests/http/showcase.yml +++ b/tests/http/showcase.yml @@ -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