diff --git a/juniper/src/http/mod.rs b/juniper/src/http/mod.rs index 9b5bbde3..d49f286c 100644 --- a/juniper/src/http/mod.rs +++ b/juniper/src/http/mod.rs @@ -217,6 +217,120 @@ where } } +/// Simple wrapper around GraphQLRequest to allow the handling of Batch requests +#[derive(Debug, serde_derive::Deserialize, PartialEq)] +#[serde(untagged)] +#[serde(bound = "InputValue: Deserialize<'de>")] +pub enum GraphQLBatchRequest +where + S: ScalarValue, +{ + /// A single operation request. + Single(GraphQLRequest), + /// A batch operation request. + Batch(Vec>), +} + +impl GraphQLBatchRequest +where + S: ScalarValue, +{ + /// Execute a GraphQL batch request synchronously using the specified schema and context + /// + /// This is a simple wrapper around the `execute_sync` function exposed in GraphQLRequest. + pub fn execute_sync<'a, CtxT, QueryT, MutationT, SubscriptionT>( + &'a self, + root_node: &'a crate::RootNode, + context: &CtxT, + ) -> GraphQLBatchResponse<'a, S> + where + QueryT: crate::GraphQLType, + MutationT: crate::GraphQLType, + SubscriptionT: crate::GraphQLType, + SubscriptionT::TypeInfo: Send + Sync, + CtxT: Send + Sync, + { + match *self { + GraphQLBatchRequest::Single(ref request) => { + GraphQLBatchResponse::Single(request.execute_sync(root_node, context)) + } + GraphQLBatchRequest::Batch(ref requests) => GraphQLBatchResponse::Batch( + requests + .iter() + .map(|request| request.execute_sync(root_node, context)) + .collect(), + ), + } + } + + /// Executes a GraphQL request using the specified schema and context + /// + /// This is a simple wrapper around the `execute` function exposed in + /// GraphQLRequest + pub async fn execute<'a, CtxT, QueryT, MutationT, SubscriptionT>( + &'a self, + root_node: &'a crate::RootNode<'a, QueryT, MutationT, SubscriptionT, S>, + context: &'a CtxT, + ) -> GraphQLBatchResponse<'a, S> + where + QueryT: crate::GraphQLTypeAsync + Send + Sync, + QueryT::TypeInfo: Send + Sync, + MutationT: crate::GraphQLTypeAsync + Send + Sync, + MutationT::TypeInfo: Send + Sync, + SubscriptionT: crate::GraphQLSubscriptionType + Send + Sync, + SubscriptionT::TypeInfo: Send + Sync, + CtxT: Send + Sync, + S: Send + Sync, + { + match *self { + GraphQLBatchRequest::Single(ref request) => { + let res = request.execute(root_node, context).await; + GraphQLBatchResponse::Single(res) + } + GraphQLBatchRequest::Batch(ref requests) => { + let futures = requests + .iter() + .map(|request| request.execute(root_node, context)) + .collect::>(); + let responses = futures::future::join_all(futures).await; + + GraphQLBatchResponse::Batch(responses) + } + } + } +} + +/// Simple wrapper around the result (GraphQLResponse) from executing a GraphQLBatchRequest +/// +/// This struct implements Serialize, so you can simply serialize this +/// to JSON and send it over the wire. use the `is_ok` to determine +/// wheter to send a 200 or 400 HTTP status code. +#[derive(serde_derive::Serialize)] +#[serde(untagged)] +pub enum GraphQLBatchResponse<'a, S = DefaultScalarValue> +where + S: ScalarValue, +{ + /// Result of a single operation in a GraphQL request. + Single(GraphQLResponse<'a, S>), + /// Result of a batch operation in a GraphQL request. + Batch(Vec>), +} + +impl<'a, S> GraphQLBatchResponse<'a, S> +where + S: ScalarValue, +{ + /// Returns if all the GraphQLResponse in this operation are ok, + /// you can use it to determine wheter to send a 200 or 400 HTTP status code. + pub fn is_ok(&self) -> bool { + match self { + GraphQLBatchResponse::Single(res) => res.is_ok(), + GraphQLBatchResponse::Batch(reses) => reses.iter().all(|res| res.is_ok()), + } + } +} + #[cfg(any(test, feature = "expose-test-schema"))] #[allow(missing_docs)] pub mod tests { diff --git a/juniper_warp/CHANGELOG.md b/juniper_warp/CHANGELOG.md index 7ce363ff..dbfb9967 100644 --- a/juniper_warp/CHANGELOG.md +++ b/juniper_warp/CHANGELOG.md @@ -1,6 +1,8 @@ # master - Compatibility with the latest `juniper`. +- Changed the implementation place of GraphQLBatchRequest and GraphQLBatchResponse in `juniper_warp` +to `juniper` to be reused in other http integrations, since this implementation was private. ## Breaking Changes diff --git a/juniper_warp/src/lib.rs b/juniper_warp/src/lib.rs index 69e5ed7d..458c9fac 100644 --- a/juniper_warp/src/lib.rs +++ b/juniper_warp/src/lib.rs @@ -43,106 +43,10 @@ Check the LICENSE file for details. use std::{pin::Pin, sync::Arc}; use futures::{Future, FutureExt as _, TryFutureExt}; -use juniper::{DefaultScalarValue, InputValue, ScalarValue}; -use serde::Deserialize; +use juniper::{http::GraphQLBatchRequest, ScalarValue}; use tokio::task; use warp::{filters::BoxedFilter, Filter}; -#[derive(Debug, serde_derive::Deserialize, PartialEq)] -#[serde(untagged)] -#[serde(bound = "InputValue: Deserialize<'de>")] -enum GraphQLBatchRequest -where - S: ScalarValue, -{ - Single(juniper::http::GraphQLRequest), - Batch(Vec>), -} - -impl GraphQLBatchRequest -where - S: ScalarValue, -{ - pub fn execute_sync<'a, CtxT, QueryT, MutationT, SubscriptionT>( - &'a self, - root_node: &'a juniper::RootNode, - context: &CtxT, - ) -> GraphQLBatchResponse<'a, S> - where - QueryT: juniper::GraphQLType, - MutationT: juniper::GraphQLType, - SubscriptionT: juniper::GraphQLType, - SubscriptionT::TypeInfo: Send + Sync, - CtxT: Send + Sync, - { - match *self { - GraphQLBatchRequest::Single(ref request) => { - GraphQLBatchResponse::Single(request.execute_sync(root_node, context)) - } - GraphQLBatchRequest::Batch(ref requests) => GraphQLBatchResponse::Batch( - requests - .iter() - .map(|request| request.execute_sync(root_node, context)) - .collect(), - ), - } - } - - pub async fn execute<'a, CtxT, QueryT, MutationT, SubscriptionT>( - &'a self, - root_node: &'a juniper::RootNode<'a, QueryT, MutationT, SubscriptionT, S>, - context: &'a CtxT, - ) -> GraphQLBatchResponse<'a, S> - where - QueryT: juniper::GraphQLTypeAsync + Send + Sync, - QueryT::TypeInfo: Send + Sync, - MutationT: juniper::GraphQLTypeAsync + Send + Sync, - MutationT::TypeInfo: Send + Sync, - SubscriptionT: juniper::GraphQLSubscriptionType + Send + Sync, - SubscriptionT::TypeInfo: Send + Sync, - CtxT: Send + Sync, - S: Send + Sync, - { - match *self { - GraphQLBatchRequest::Single(ref request) => { - let res = request.execute(root_node, context).await; - GraphQLBatchResponse::Single(res) - } - GraphQLBatchRequest::Batch(ref requests) => { - let futures = requests - .iter() - .map(|request| request.execute(root_node, context)) - .collect::>(); - let responses = futures::future::join_all(futures).await; - - GraphQLBatchResponse::Batch(responses) - } - } - } -} - -#[derive(serde_derive::Serialize)] -#[serde(untagged)] -enum GraphQLBatchResponse<'a, S = DefaultScalarValue> -where - S: ScalarValue, -{ - Single(juniper::http::GraphQLResponse<'a, S>), - Batch(Vec>), -} - -impl<'a, S> GraphQLBatchResponse<'a, S> -where - S: ScalarValue, -{ - fn is_ok(&self) -> bool { - match self { - GraphQLBatchResponse::Single(res) => res.is_ok(), - GraphQLBatchResponse::Batch(reses) => reses.iter().all(|res| res.is_ok()), - } - } -} - /// Make a filter for graphql queries/mutations. /// /// The `schema` argument is your juniper schema.