From bb582e998cbc57117dfa56befcf51fff7bdb4a22 Mon Sep 17 00:00:00 2001 From: Graeme Coupar Date: Thu, 15 Aug 2019 16:06:31 +0100 Subject: [PATCH 1/5] Update to support async branch of rocket. --- juniper_rocket/Cargo.toml | 3 ++- juniper_rocket/src/lib.rs | 47 +++++++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/juniper_rocket/Cargo.toml b/juniper_rocket/Cargo.toml index 8fd1a618..c572a079 100644 --- a/juniper_rocket/Cargo.toml +++ b/juniper_rocket/Cargo.toml @@ -17,7 +17,8 @@ serde_json = { version = "1.0.2" } serde_derive = { version = "1.0.2" } juniper = { version = "0.13.1" , default-features = false, path = "../juniper"} -rocket = { version = "0.4.0" } +futures-preview = { version = "0.3.0-alpha.18", features = ["compat"] } +rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" } [dev-dependencies.juniper] version = "0.13.1" diff --git a/juniper_rocket/src/lib.rs b/juniper_rocket/src/lib.rs index a016f132..a3760aa3 100644 --- a/juniper_rocket/src/lib.rs +++ b/juniper_rocket/src/lib.rs @@ -37,7 +37,7 @@ Check the LICENSE file for details. */ #![doc(html_root_url = "https://docs.rs/juniper_rocket/0.2.0")] -#![feature(decl_macro, proc_macro_hygiene)] +#![feature(decl_macro, proc_macro_hygiene, async_await)] use std::{ error::Error, @@ -45,10 +45,10 @@ use std::{ }; use rocket::{ - data::{FromDataSimple, Outcome as FromDataOutcome}, + data::{FromDataFuture, FromDataSimple}, http::{ContentType, RawStr, Status}, request::{FormItems, FromForm, FromFormValue}, - response::{content, Responder, Response}, + response::{content, Responder, Response, ResultFuture}, Data, Outcome::{Failure, Forward, Success}, Request, @@ -331,38 +331,47 @@ where } } +const BODY_LIMIT: u64 = 1024 * 100; + impl FromDataSimple for GraphQLRequest where S: ScalarValue, { type Error = String; - fn from_data(request: &Request, data: Data) -> FromDataOutcome { + fn from_data(request: &Request, data: Data) -> FromDataFuture<'static, Self, Self::Error> { + use futures::io::AsyncReadExt; + use rocket::AsyncReadExt as _; if !request.content_type().map_or(false, |ct| ct.is_json()) { - return Forward(data); + return Box::pin(async move { Forward(data) }); } - let mut body = String::new(); - if let Err(e) = data.open().read_to_string(&mut body) { - return Failure((Status::InternalServerError, format!("{:?}", e))); - } + Box::pin(async move { + let mut body = String::new(); + let mut reader = data.open().take(BODY_LIMIT); + if let Err(e) = reader.read_to_string(&mut body).await { + return Failure((Status::InternalServerError, format!("{:?}", e))); + } - match serde_json::from_str(&body) { - Ok(value) => Success(GraphQLRequest(value)), - Err(failure) => return Failure((Status::BadRequest, format!("{}", failure))), - } + match serde_json::from_str(&body) { + Ok(value) => Success(GraphQLRequest(value)), + Err(failure) => Failure((Status::BadRequest, format!("{}", failure))), + } + }) } } impl<'r> Responder<'r> for GraphQLResponse { - fn respond_to(self, _: &Request) -> Result, Status> { + fn respond_to(self, _: &Request) -> ResultFuture<'r> { let GraphQLResponse(status, body) = self; - Ok(Response::build() - .header(ContentType::new("application", "json")) - .status(status) - .sized_body(Cursor::new(body)) - .finalize()) + Box::pin(async move { + Ok(Response::build() + .header(ContentType::new("application", "json")) + .status(status) + .sized_body(Cursor::new(body)) + .finalize()) + }) } } From 1d7ee1bcb1c692ab9b46d63ea642ff7c10725131 Mon Sep 17 00:00:00 2001 From: Graeme Coupar Date: Wed, 21 Aug 2019 10:22:16 +0100 Subject: [PATCH 2/5] Some more updates to juniper_rocket --- juniper_rocket/Cargo.toml | 5 +++- juniper_rocket/src/lib.rs | 56 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/juniper_rocket/Cargo.toml b/juniper_rocket/Cargo.toml index c572a079..88aebbfe 100644 --- a/juniper_rocket/Cargo.toml +++ b/juniper_rocket/Cargo.toml @@ -11,13 +11,16 @@ documentation = "https://docs.rs/juniper_rocket" repository = "https://github.com/graphql-rust/juniper" edition = "2018" +[features] +async = [ "juniper/async", "futures03" ] + [dependencies] serde = { version = "1.0.2" } serde_json = { version = "1.0.2" } serde_derive = { version = "1.0.2" } juniper = { version = "0.13.1" , default-features = false, path = "../juniper"} -futures-preview = { version = "0.3.0-alpha.18", features = ["compat"] } +futures03 = { version = "0.3.0-alpha.18", optional = true, package = "futures-preview", features = ["compat"] } rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" } [dev-dependencies.juniper] diff --git a/juniper_rocket/src/lib.rs b/juniper_rocket/src/lib.rs index a3760aa3..c50f747d 100644 --- a/juniper_rocket/src/lib.rs +++ b/juniper_rocket/src/lib.rs @@ -37,7 +37,9 @@ Check the LICENSE file for details. */ #![doc(html_root_url = "https://docs.rs/juniper_rocket/0.2.0")] -#![feature(decl_macro, proc_macro_hygiene, async_await)] +#![feature(decl_macro, proc_macro_hygiene)] + +#![cfg_attr(feature = "async", feature(async_await, async_closure))] use std::{ error::Error, @@ -61,6 +63,9 @@ use juniper::{ ScalarValue, }; +#[cfg(feature = "async")] +use futures03::future::{FutureExt, TryFutureExt}; + #[derive(Debug, serde_derive::Deserialize, PartialEq)] #[serde(untagged)] #[serde(bound = "InputValue: Deserialize<'de>")] @@ -109,6 +114,31 @@ where } } + #[cfg(feature = "async")] + pub async fn execute_async<'a, CtxT, QueryT, MutationT>( + &'a self, + root_node: &'a RootNode, + context: &CtxT, + ) -> GraphQLBatchResponse<'a, S> + where + QueryT: GraphQLType, + MutationT: GraphQLType, + { + match self { + &GraphQLBatchRequest::Single(ref request) => { + GraphQLBatchResponse::Single(request.execute_async(root_node, context).await) + } + &GraphQLBatchRequest::Batch(ref requests) => GraphQLBatchResponse::Batch( + let futures = requests + .iter() + .map(|request| request.execute(root_node, context)) + .collect::>(), + + let responses = futures03::future::join_all(futures).await; + ), + } + } + pub fn operation_names(&self) -> Vec> { match self { GraphQLBatchRequest::Single(req) => vec![req.operation_name()], @@ -184,6 +214,28 @@ where GraphQLResponse(status, json) } + /// Asynchronously execute an incoming GraphQL query + #[cfg(feature = "async")] + pub async fn execute_async( + &self, + root_node: &RootNode, + context: &CtxT, + ) -> GraphQLResponse + where + QueryT: GraphQLType, + MutationT: GraphQLType, + { + let response = self.0.execute_async(root_node, context).await; + let status = if response.is_ok() { + Status::Ok + } else { + Status::BadRequest + }; + let json = serde_json::to_string(&response).unwrap(); + + GraphQLResponse(status, json) + } + /// Returns the operation names associated with this request. /// /// For batch requests there will be multiple names. @@ -340,7 +392,7 @@ where type Error = String; fn from_data(request: &Request, data: Data) -> FromDataFuture<'static, Self, Self::Error> { - use futures::io::AsyncReadExt; + use futures03::io::AsyncReadExt; use rocket::AsyncReadExt as _; if !request.content_type().map_or(false, |ct| ct.is_json()) { return Box::pin(async move { Forward(data) }); From 6c54f05efb5dc77467517c57e732ba649aeff564 Mon Sep 17 00:00:00 2001 From: Graeme Coupar Date: Wed, 21 Aug 2019 12:40:43 +0100 Subject: [PATCH 3/5] Update for latest rust nightly --- juniper_rocket/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/juniper_rocket/src/lib.rs b/juniper_rocket/src/lib.rs index c50f747d..9ebe0bbf 100644 --- a/juniper_rocket/src/lib.rs +++ b/juniper_rocket/src/lib.rs @@ -117,7 +117,7 @@ where #[cfg(feature = "async")] pub async fn execute_async<'a, CtxT, QueryT, MutationT>( &'a self, - root_node: &'a RootNode, + root_node: &'a RootNode<'_, QueryT, MutationT, S>, context: &CtxT, ) -> GraphQLBatchResponse<'a, S> where @@ -128,14 +128,14 @@ where &GraphQLBatchRequest::Single(ref request) => { GraphQLBatchResponse::Single(request.execute_async(root_node, context).await) } - &GraphQLBatchRequest::Batch(ref requests) => GraphQLBatchResponse::Batch( + &GraphQLBatchRequest::Batch(ref requests) => { let futures = requests .iter() - .map(|request| request.execute(root_node, context)) - .collect::>(), + .map(|request| request.execute_async(root_node, context)) + .collect::>(); - let responses = futures03::future::join_all(futures).await; - ), + GraphQLBatchResponse::Batch(futures03::future::join_all(futures).await) + }, } } @@ -218,7 +218,7 @@ where #[cfg(feature = "async")] pub async fn execute_async( &self, - root_node: &RootNode, + root_node: &RootNode<'_, QueryT, MutationT, S>, context: &CtxT, ) -> GraphQLResponse where From 66cab5334528adc4b71da1f1db36eb11956ccbfa Mon Sep 17 00:00:00 2001 From: Graeme Coupar Date: Wed, 21 Aug 2019 13:16:19 +0100 Subject: [PATCH 4/5] Fix the rest of the rocket stuff --- juniper_rocket/Cargo.toml | 4 ++-- juniper_rocket/src/lib.rs | 42 +++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/juniper_rocket/Cargo.toml b/juniper_rocket/Cargo.toml index 88aebbfe..737aca70 100644 --- a/juniper_rocket/Cargo.toml +++ b/juniper_rocket/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/graphql-rust/juniper" edition = "2018" [features] -async = [ "juniper/async", "futures03" ] +async = [ "juniper/async" ] [dependencies] serde = { version = "1.0.2" } @@ -20,7 +20,7 @@ serde_json = { version = "1.0.2" } serde_derive = { version = "1.0.2" } juniper = { version = "0.13.1" , default-features = false, path = "../juniper"} -futures03 = { version = "0.3.0-alpha.18", optional = true, package = "futures-preview", features = ["compat"] } +futures03 = { version = "0.3.0-alpha.18", package = "futures-preview", features = ["compat"] } rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" } [dev-dependencies.juniper] diff --git a/juniper_rocket/src/lib.rs b/juniper_rocket/src/lib.rs index 9ebe0bbf..891a3723 100644 --- a/juniper_rocket/src/lib.rs +++ b/juniper_rocket/src/lib.rs @@ -38,12 +38,11 @@ Check the LICENSE file for details. #![doc(html_root_url = "https://docs.rs/juniper_rocket/0.2.0")] #![feature(decl_macro, proc_macro_hygiene)] - #![cfg_attr(feature = "async", feature(async_await, async_closure))] use std::{ error::Error, - io::{Cursor, Read}, + io::Cursor, }; use rocket::{ @@ -63,6 +62,9 @@ use juniper::{ ScalarValue, }; +#[cfg(feature = "async")] +use juniper::GraphQLTypeAsync; + #[cfg(feature = "async")] use futures03::future::{FutureExt, TryFutureExt}; @@ -71,7 +73,7 @@ use futures03::future::{FutureExt, TryFutureExt}; #[serde(bound = "InputValue: Deserialize<'de>")] enum GraphQLBatchRequest where - S: ScalarValue, + S: ScalarValue + Sync + Send, { Single(http::GraphQLRequest), Batch(Vec>), @@ -81,7 +83,7 @@ where #[serde(untagged)] enum GraphQLBatchResponse<'a, S = DefaultScalarValue> where - S: ScalarValue, + S: ScalarValue + Sync + Send, { Single(http::GraphQLResponse<'a, S>), Batch(Vec>), @@ -89,7 +91,7 @@ where impl GraphQLBatchRequest where - S: ScalarValue, + S: ScalarValue + Send + Sync, for<'b> &'b S: ScalarRefValue<'b>, { pub fn execute<'a, CtxT, QueryT, MutationT>( @@ -118,11 +120,14 @@ where pub async fn execute_async<'a, CtxT, QueryT, MutationT>( &'a self, root_node: &'a RootNode<'_, QueryT, MutationT, S>, - context: &CtxT, + context: &'a CtxT, ) -> GraphQLBatchResponse<'a, S> where - QueryT: GraphQLType, - MutationT: GraphQLType, + QueryT: GraphQLTypeAsync + Send + Sync, + QueryT::TypeInfo: Send + Sync, + MutationT: GraphQLTypeAsync + Send + Sync, + MutationT::TypeInfo: Send + Sync, + CtxT: Send + Sync, { match self { &GraphQLBatchRequest::Single(ref request) => { @@ -135,7 +140,7 @@ where .collect::>(); GraphQLBatchResponse::Batch(futures03::future::join_all(futures).await) - }, + } } } @@ -151,7 +156,7 @@ where impl<'a, S> GraphQLBatchResponse<'a, S> where - S: ScalarValue, + S: ScalarValue + Send + Sync, { fn is_ok(&self) -> bool { match self { @@ -171,7 +176,7 @@ where #[derive(Debug, PartialEq)] pub struct GraphQLRequest(GraphQLBatchRequest) where - S: ScalarValue; + S: ScalarValue + Send + Sync; /// Simple wrapper around the result of executing a GraphQL query pub struct GraphQLResponse(pub Status, pub String); @@ -190,7 +195,7 @@ pub fn playground_source(graphql_endpoint_url: &str) -> content::Html { impl GraphQLRequest where - S: ScalarValue, + S: ScalarValue + Sync + Send, for<'b> &'b S: ScalarRefValue<'b>, { /// Execute an incoming GraphQL query @@ -222,8 +227,11 @@ where context: &CtxT, ) -> GraphQLResponse where - QueryT: GraphQLType, - MutationT: GraphQLType, + QueryT: GraphQLTypeAsync + Send + Sync, + QueryT::TypeInfo: Send + Sync, + MutationT: GraphQLTypeAsync + Send + Sync, + MutationT::TypeInfo: Send + Sync, + CtxT: Send + Sync, { let response = self.0.execute_async(root_node, context).await; let status = if response.is_ok() { @@ -301,7 +309,7 @@ impl GraphQLResponse { impl<'f, S> FromForm<'f> for GraphQLRequest where - S: ScalarValue, + S: ScalarValue + Send + Sync, { type Error = String; @@ -372,7 +380,7 @@ where impl<'v, S> FromFormValue<'v> for GraphQLRequest where - S: ScalarValue, + S: ScalarValue + Send + Sync, { type Error = String; @@ -387,7 +395,7 @@ const BODY_LIMIT: u64 = 1024 * 100; impl FromDataSimple for GraphQLRequest where - S: ScalarValue, + S: ScalarValue + Send + Sync, { type Error = String; From ab37f647dbbd1f33892b63e3964c4515ccdf1821 Mon Sep 17 00:00:00 2001 From: Graeme Coupar Date: Thu, 22 Aug 2019 10:38:01 +0100 Subject: [PATCH 5/5] Fix juniper issue --- juniper/src/types/async_await.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs index 12369bef..4297c630 100644 --- a/juniper/src/types/async_await.rs +++ b/juniper/src/types/async_await.rs @@ -5,6 +5,8 @@ use crate::value::{Object, ScalarRefValue, ScalarValue, Value}; use crate::executor::{ExecutionResult, Executor}; use crate::parser::Spanning; +use crate::BoxFuture; + use super::base::{is_excluded, merge_key_into, Arguments, GraphQLType}; pub trait GraphQLTypeAsync: GraphQLType + Send + Sync