From d246233952eab0749e8946249b1140904c36a479 Mon Sep 17 00:00:00 2001 From: vkill Date: Tue, 12 May 2020 09:11:45 +0800 Subject: [PATCH] async-graphql-tide: support Extension Traits, ref https://blog.yoshuawuyts.com/tide/ --- async-graphql-tide/Cargo.toml | 1 + async-graphql-tide/src/lib.rs | 122 +++++++++++++++++++++++----- async-graphql-tide/tests/graphql.rs | 3 +- 3 files changed, 106 insertions(+), 20 deletions(-) diff --git a/async-graphql-tide/Cargo.toml b/async-graphql-tide/Cargo.toml index 2251fab0..d009a6c9 100644 --- a/async-graphql-tide/Cargo.toml +++ b/async-graphql-tide/Cargo.toml @@ -15,6 +15,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] async-graphql = { path = "..", version = "1.11.3" } tide = "0.8" +async-trait = "0.1.30" [dev-dependencies] async-std = "1.5.0" diff --git a/async-graphql-tide/src/lib.rs b/async-graphql-tide/src/lib.rs index a55fccf4..4ce5302a 100644 --- a/async-graphql-tide/src/lib.rs +++ b/async-graphql-tide/src/lib.rs @@ -8,6 +8,7 @@ use async_graphql::http::GQLResponse; use async_graphql::{ IntoQueryBuilder, IntoQueryBuilderOpts, ObjectType, QueryBuilder, Schema, SubscriptionType, }; +use async_trait::async_trait; use tide::{http::headers, Request, Response, Status, StatusCode}; /// GraphQL request handler @@ -53,7 +54,7 @@ where Mutation: ObjectType + Send + Sync + 'static, Subscription: SubscriptionType + Send + Sync + 'static, TideState: Send + Sync + 'static, - F: Fn(QueryBuilder) -> QueryBuilder, + F: Fn(QueryBuilder) -> QueryBuilder + Send, { graphql_opts(req, schema, query_builder_configuration, Default::default()).await } @@ -70,24 +71,107 @@ where Mutation: ObjectType + Send + Sync + 'static, Subscription: SubscriptionType + Send + Sync + 'static, TideState: Send + Sync + 'static, - F: Fn(QueryBuilder) -> QueryBuilder, + F: Fn(QueryBuilder) -> QueryBuilder + Send, { - let content_type = req - .header(&headers::CONTENT_TYPE) - .and_then(|values| values.first().map(|value| value.to_string())); - - let mut query_builder = (content_type, req) - .into_query_builder_opts(&opts) + req.graphql_opts(schema, query_builder_configuration, opts) .await - .status(StatusCode::BadRequest)?; - - query_builder = query_builder_configuration(query_builder); - - let query_response = query_builder.execute(&schema).await; - - let gql_response = GQLResponse(query_response); - - let resp = Response::new(StatusCode::Ok).body_json(&gql_response)?; - - Ok(resp) +} + +/// Tide request extension +/// +#[async_trait] +pub trait RequestExt: Sized { + /// ```no_run + /// use async_graphql::*; + /// use async_std::task; + /// use tide::Request; + /// use async_graphql_tide::RequestExt as _; + /// + /// struct QueryRoot; + /// #[Object] + /// impl QueryRoot { + /// #[field(desc = "Returns the sum of a and b")] + /// async fn add(&self, a: i32, b: i32) -> i32 { + /// a + b + /// } + /// } + /// + /// fn main() -> std::result::Result<(), Box> { + /// task::block_on(async { + /// let mut app = tide::new(); + /// app.at("/").post(|req: Request<()>| async move { + /// let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).finish(); + /// req.graphql(schema, |query_builder| query_builder).await + /// }); + /// app.listen("0.0.0.0:8000").await?; + /// + /// Ok(()) + /// }) + /// } + /// ``` + async fn graphql( + self, + schema: Schema, + query_builder_configuration: F, + ) -> tide::Result + where + Query: ObjectType + Send + Sync + 'static, + Mutation: ObjectType + Send + Sync + 'static, + Subscription: SubscriptionType + Send + Sync + 'static, + State: Send + Sync + 'static, + F: Fn(QueryBuilder) -> QueryBuilder + Send, + { + self.graphql_opts(schema, query_builder_configuration, Default::default()) + .await + } + + /// Similar to graphql, but you can set the options `IntoQueryBuilderOpts`. + async fn graphql_opts( + self, + schema: Schema, + query_builder_configuration: F, + opts: IntoQueryBuilderOpts, + ) -> tide::Result + where + Query: ObjectType + Send + Sync + 'static, + Mutation: ObjectType + Send + Sync + 'static, + Subscription: SubscriptionType + Send + Sync + 'static, + State: Send + Sync + 'static, + F: Fn(QueryBuilder) -> QueryBuilder + Send; +} + +#[async_trait] +impl RequestExt for Request { + async fn graphql_opts( + self, + schema: Schema, + query_builder_configuration: F, + opts: IntoQueryBuilderOpts, + ) -> tide::Result + where + Query: ObjectType + Send + Sync + 'static, + Mutation: ObjectType + Send + Sync + 'static, + Subscription: SubscriptionType + Send + Sync + 'static, + State: Send + Sync + 'static, + F: Fn(QueryBuilder) -> QueryBuilder + Send, + { + let content_type = self + .header(&headers::CONTENT_TYPE) + .and_then(|values| values.first().map(|value| value.to_string())); + + let mut query_builder = (content_type, self) + .into_query_builder_opts(&opts) + .await + .status(StatusCode::BadRequest)?; + + query_builder = query_builder_configuration(query_builder); + + let query_response = query_builder.execute(&schema).await; + + let gql_response = GQLResponse(query_response); + + let resp = Response::new(StatusCode::Ok).body_json(&gql_response)?; + + Ok(resp) + } } diff --git a/async-graphql-tide/tests/graphql.rs b/async-graphql-tide/tests/graphql.rs index 52638dac..1a7c67ad 100644 --- a/async-graphql-tide/tests/graphql.rs +++ b/async-graphql-tide/tests/graphql.rs @@ -6,6 +6,7 @@ use std::time::Duration; use tide::Request; use async_graphql::*; +use async_graphql_tide::RequestExt as _; type Result = std::result::Result>; @@ -27,7 +28,7 @@ fn quickstart() -> Result<()> { let mut app = tide::new(); app.at("/").post(|req: Request<()>| async move { let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).finish(); - async_graphql_tide::graphql(req, schema, |query_builder| query_builder).await + req.graphql(schema, |query_builder| query_builder).await }); app.listen(&listen_addr).await?;