From a43e437a387d28f380d4c04683e9c351fb7b8cae Mon Sep 17 00:00:00 2001 From: Sunli Date: Fri, 11 Sep 2020 22:37:48 +0800 Subject: [PATCH] async-graphql-warp 2.0 --- Cargo.toml | 1 - integrations/lambda/Cargo.toml | 19 ------- integrations/lambda/src/lib.rs | 40 --------------- integrations/rocket/Cargo.toml | 1 + integrations/tide/src/lib.rs | 6 +-- integrations/warp/src/lib.rs | 90 ++++++++++++++++++---------------- 6 files changed, 52 insertions(+), 105 deletions(-) delete mode 100644 integrations/lambda/Cargo.toml delete mode 100644 integrations/lambda/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 66ed0b32..b025789b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,5 @@ members = [ "integrations/warp", "integrations/tide", "integrations/rocket", -# "integrations/lambda", "benchmark", ] diff --git a/integrations/lambda/Cargo.toml b/integrations/lambda/Cargo.toml deleted file mode 100644 index de4ffae8..00000000 --- a/integrations/lambda/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "async-graphql-lambda" -version = "1.18.0" -authors = ["Sunli "] -edition = "2018" -description = "async-graphql for AWS Lambda" -publish = true -license = "MIT/Apache-2.0" -documentation = "https://docs.rs/async-graphql/" -homepage = "https://github.com/async-graphql/async-graphql" -repository = "https://github.com/async-graphql/async-graphql" -keywords = ["futures", "async", "graphql"] -categories = ["network-programming", "asynchronous"] - -[dependencies] -async-graphql = { path = "../..", version = "1.18.0" } -lambda_http = { git = "https://github.com/awslabs/aws-lambda-rust-runtime" } -futures = "0.3.0" -async-trait = "0.1.30" diff --git a/integrations/lambda/src/lib.rs b/integrations/lambda/src/lib.rs deleted file mode 100644 index 348df25a..00000000 --- a/integrations/lambda/src/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Async-graphql integration with AWS lambda -//! -#![forbid(unsafe_code)] - -use async_graphql::{IntoQueryBuilder, IntoQueryBuilderOpts, ParseRequestError, QueryBuilder}; -use futures::io::AllowStdIo; -use lambda_http::Request; -use std::io::Cursor; - -/// Lambda request extension -/// -#[async_trait::async_trait] -pub trait GQLRequestExt { - /// Convert a query to `async_graphql::QueryBuilder`. - async fn graphql(&self) -> Result { - self.graphql_opts(Default::default()).await - } - - /// Similar to graphql, but you can set the options `IntoQueryBuilderOpts`. - async fn graphql_opts( - &self, - opts: IntoQueryBuilderOpts, - ) -> Result; -} - -#[async_trait::async_trait] -impl GQLRequestExt for Request { - async fn graphql_opts( - &self, - opts: IntoQueryBuilderOpts, - ) -> Result { - let ct = self - .headers() - .get("content-type") - .and_then(|value| value.to_str().ok()); - (ct, AllowStdIo::new(Cursor::new(self.body().to_vec()))) - .into_query_builder_opts(&opts) - .await - } -} diff --git a/integrations/rocket/Cargo.toml b/integrations/rocket/Cargo.toml index 16d5ae71..6359cf30 100644 --- a/integrations/rocket/Cargo.toml +++ b/integrations/rocket/Cargo.toml @@ -4,6 +4,7 @@ version = "1.18.0" authors = ["Daniel Wiesenberg "] edition = "2018" description = "async-graphql for Rocket.rs" +# Waiting for Rocket 0.5 release. #publish = true license = "MIT/Apache-2.0" documentation = "https://docs.rs/async-graphql/" diff --git a/integrations/tide/src/lib.rs b/integrations/tide/src/lib.rs index a01871f3..bd70f964 100644 --- a/integrations/tide/src/lib.rs +++ b/integrations/tide/src/lib.rs @@ -62,7 +62,7 @@ where graphql_opts(req, schema, configuration, Default::default()).await } -/// Similar to graphql, but you can set the options `MultipartOptions`. +/// Similar to graphql, but you can set the options `async_graphql::MultipartOptions`. pub async fn graphql_opts( req: Request, schema: Schema, @@ -84,12 +84,12 @@ where /// #[async_trait] pub trait RequestExt: Sized { - /// Convert a query to `async_graphql::QueryBuilder`. + /// Convert a query to `async_graphql::Request`. async fn body_graphql(self) -> tide::Result { self.body_graphql_opts(Default::default()).await } - /// Similar to graphql, but you can set the options `IntoQueryBuilderOpts`. + /// Similar to `RequestExt::body_graphql`, but you can set the options `async_graphql::MultipartOptions`. async fn body_graphql_opts( self, opts: MultipartOptions, diff --git a/integrations/warp/src/lib.rs b/integrations/warp/src/lib.rs index 2a77feed..e4e5d772 100644 --- a/integrations/warp/src/lib.rs +++ b/integrations/warp/src/lib.rs @@ -5,11 +5,8 @@ #![allow(clippy::needless_doctest_main)] #![forbid(unsafe_code)] -use async_graphql::http::{GQLRequest, StreamBody}; -use async_graphql::{ - Data, FieldResult, IntoQueryBuilder, ObjectType, QueryBuilder, ReceiveMultipartOptions, - Response, Schema, SubscriptionType, WebSocketTransport, -}; +use async_graphql::http::{GQLRequest, MultipartOptions, StreamBody}; +use async_graphql::{Data, FieldResult, ObjectType, Schema, SubscriptionType}; use futures::select; use futures::{SinkExt, StreamExt}; use hyper::Method; @@ -35,9 +32,10 @@ impl Reject for BadRequest {} /// GraphQL request filter /// -/// It outputs a tuple containing the `Schema` and `QuertBuilder`. +/// It outputs a tuple containing the `async_graphql::Schema` and `async_graphql::Request`. /// /// # Examples +/// /// *[Full Example]()* /// /// ```no_run @@ -57,18 +55,24 @@ impl Reject for BadRequest {} /// } /// } /// +/// type MySchema = Schema; +/// /// #[tokio::main] /// async fn main() { /// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription); -/// let filter = async_graphql_warp::graphql(schema).and_then(|(schema, builder): (_, QueryBuilder)| async move { -/// Ok::<_, Infallible>(GQLResponse::from(builder.execute(&schema).await)) +/// let filter = async_graphql_warp::graphql(schema). +/// and_then(|(schema, request): (MySchema, async_graphql::Request)| async move { +/// Ok::<_, Infallible>(GQLResponse::from(schema.execute(request).await)) /// }); /// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await; /// } /// ``` pub fn graphql( schema: Schema, -) -> BoxedFilter<((Schema, QueryBuilder),)> +) -> BoxedFilter<(( + Schema, + async_graphql::Request, +),)> where Query: ObjectType + Send + Sync + 'static, Mutation: ObjectType + Send + Sync + 'static, @@ -77,11 +81,14 @@ where graphql_opts(schema, Default::default()) } -/// Similar to graphql, but you can set the options `IntoQueryBuilderOpts`. +/// Similar to graphql, but you can set the options `async_graphql::MultipartOptions`. pub fn graphql_opts( schema: Schema, - opts: ReceiveMultipartOptions, -) -> BoxedFilter<((Schema, QueryBuilder),)> + opts: MultipartOptions, +) -> BoxedFilter<(( + Schema, + async_graphql::Request, +),)> where Query: ObjectType + Send + Sync + 'static, Mutation: ObjectType + Send + Sync + 'static, @@ -100,22 +107,19 @@ where query: String, content_type, body, - opts: Arc, + opts: Arc, schema| async move { if method == Method::GET { - let gql_request: GQLRequest = serde_urlencoded::from_str(&query) + let request: GQLRequest = serde_urlencoded::from_str(&query) .map_err(|err| warp::reject::custom(BadRequest(err.into())))?; - let builder = gql_request - .into_query_builder_opts(&opts) - .await - .map_err(|err| warp::reject::custom(BadRequest(err.into())))?; - Ok::<_, Rejection>((schema, builder)) + Ok::<_, Rejection>((schema, async_graphql::Request::from(request))) } else { - let builder = (content_type, StreamBody::new(body)) - .into_query_builder_opts(&opts) - .await - .map_err(|err| warp::reject::custom(BadRequest(err.into())))?; - Ok::<_, Rejection>((schema, builder)) + let request = async_graphql::http::receive_body( + content_type, + StreamBody::new(body), + MultipartOptions::clone(&opts) + ).await.map_err(|err| warp::reject::custom(BadRequest(err.into())))?; + Ok::<_, Rejection>((schema, request)) } }, ) @@ -163,15 +167,15 @@ where Mutation: ObjectType + Sync + Send + 'static, Subscription: SubscriptionType + Send + Sync + 'static, { - graphql_subscription_with_data(schema, |_| Ok(Default::default())) + graphql_subscription_with_initializer(schema, |_| Ok(Default::default())) } /// GraphQL subscription filter /// /// Specifies that a function converts the init payload to data. -pub fn graphql_subscription_with_data( +pub fn graphql_subscription_with_initializer( schema: Schema, - init_context_data: F, + initializer: F, ) -> BoxedFilter<(impl Reply,)> where Query: ObjectType + Sync + Send + 'static, @@ -182,20 +186,23 @@ where warp::any() .and(warp::ws()) .and(warp::any().map(move || schema.clone())) - .and(warp::any().map(move || init_context_data.clone())) + .and(warp::any().map(move || initializer.clone())) .map( - |ws: warp::ws::Ws, - schema: Schema, - init_context_data: F| { + |ws: warp::ws::Ws, schema: Schema, initializer: F| { ws.on_upgrade(move |websocket| { let (mut tx, rx) = websocket.split(); let (mut stx, srx) = - schema.subscription_connection(WebSocketTransport::new(init_context_data)); + async_graphql::transports::websocket::create_with_initializer( + &schema, + initializer, + ); let mut rx = rx.fuse(); - let mut srx = srx.fuse(); + let srx = srx.fuse(); async move { + futures::pin_mut!(srx); + loop { select! { bytes = srx.next() => { @@ -231,17 +238,17 @@ where } /// GraphQL reply -pub struct GQLResponse(async_graphql::Result); +pub struct GQLResponse(async_graphql::Response); -impl From> for GQLResponse { - fn from(resp: async_graphql::Result) -> Self { +impl From for GQLResponse { + fn from(resp: async_graphql::Response) -> Self { GQLResponse(resp) } } -fn add_cache_control(http_resp: &mut Response, resp: &async_graphql::Result) { - if let Ok(Response { cache_control, .. }) = resp { - if let Some(cache_control) = cache_control.value() { +fn add_cache_control(http_resp: &mut Response, resp: &async_graphql::Response) { + if resp.is_ok() { + if let Some(cache_control) = resp.cache_control.value() { if let Ok(value) = cache_control.parse() { http_resp.headers_mut().insert("cache-control", value); } @@ -251,14 +258,13 @@ fn add_cache_control(http_resp: &mut Response, resp: &async_graphql::Result Response { - let gql_resp = async_graphql::http::GQLResponse(self.0); let mut resp = warp::reply::with_header( - warp::reply::json(&gql_resp), + warp::reply::json(&self.0), "content-type", "application/json", ) .into_response(); - add_cache_control(&mut resp, &gql_resp.0); + add_cache_control(&mut resp, &self.0); resp } }