From 37943fa30a98fb173f3ea84929d36023bf530e9b Mon Sep 17 00:00:00 2001 From: Sunli Date: Fri, 5 Jun 2020 10:18:48 +0800 Subject: [PATCH] Fix relay @defer requires a label in the second chunk. #146 --- async-graphql-warp/src/lib.rs | 14 ++++++++++---- src/http/mod.rs | 5 +++++ src/query.rs | 31 ++++++++++++++++++++++++++++++- src/subscription/ws_transport.rs | 1 + src/types/deferred.rs | 1 + src/types/streamed.rs | 1 + 6 files changed, 48 insertions(+), 5 deletions(-) diff --git a/async-graphql-warp/src/lib.rs b/async-graphql-warp/src/lib.rs index e26ef0f6..5b81b32b 100644 --- a/async-graphql-warp/src/lib.rs +++ b/async-graphql-warp/src/lib.rs @@ -93,16 +93,22 @@ where let opts = Arc::new(opts); warp::any() .and(warp::method()) - .and(warp::query::raw()) + .and(warp::query::raw().or(warp::any().map(|| String::new())).unify()) .and(warp::header::optional::("content-type")) .and(warp::body::stream()) .and(warp::any().map(move || opts.clone())) .and(warp::any().map(move || schema.clone())) .and_then( - |method, query: String, content_type, body, opts: Arc, schema| async move { + |method, + query: String, + content_type, + body, + opts: Arc, + schema| async move { if method == Method::GET { - let gql_request: GQLRequest = serde_urlencoded::from_str(&query) - .map_err(|err| warp::reject::custom(BadRequest(err.into())))?; + let gql_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 diff --git a/src/http/mod.rs b/src/http/mod.rs index 47991b80..cc58fa0a 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -61,6 +61,10 @@ impl Serialize for GQLResponse { match &self.0 { Ok(res) => { let mut map = serializer.serialize_map(None)?; + if let Some(label) = &res.label { + map.serialize_key("label")?; + map.serialize_value(label)?; + } if let Some(path) = &res.path { map.serialize_key("path")?; map.serialize_value(path)?; @@ -215,6 +219,7 @@ mod tests { #[test] fn test_response_data() { let resp = GQLResponse(Ok(QueryResponse { + label: None, path: None, data: json!({"ok": true}), extensions: None, diff --git a/src/query.rs b/src/query.rs index 4b1c3965..9d9dc0d1 100644 --- a/src/query.rs +++ b/src/query.rs @@ -9,7 +9,9 @@ use crate::{ }; use async_graphql_parser::query::OperationType; use futures::{Stream, StreamExt}; +use itertools::Itertools; use std::any::Any; +use std::borrow::Cow; use std::fs::File; use std::pin::Pin; use std::sync::atomic::AtomicUsize; @@ -41,6 +43,11 @@ pub trait IntoQueryBuilder: Sized { /// Query response #[derive(Debug)] pub struct QueryResponse { + /// Label for RelayModernQueryExecutor + /// + /// https://github.com/facebook/relay/blob/2859aa8df4df7d4d6d9eef4c9dc1134286773710/packages/relay-runtime/store/RelayModernQueryExecutor.js#L1267 + pub label: Option, + /// Path for subsequent response pub path: Option>, @@ -62,6 +69,19 @@ impl QueryResponse { } else { self.path = Some(prefix); } + + self.label = self.path.as_ref().map(|path| { + path.iter() + .map(|value| { + if let serde_json::Value::String(s) = value { + Cow::Borrowed(s.as_str()) + } else { + Cow::Owned(value.to_string()) + } + }) + .join("$") + }); + self } @@ -209,7 +229,15 @@ impl QueryBuilder { next_path_prefix.extend(defer_list.path_prefix.clone()); next_defer_list.push((next_path_prefix, fut)); } - yield res.apply_path_prefix(path_prefix); + let mut new_res = res.apply_path_prefix(path_prefix); + new_res.label = new_res.path.as_ref().map(|path| path.iter().map(|value| { + if let serde_json::Value::String(s) = value { + s.to_string() + } else { + value.to_string() + } + }).join("$")); + yield new_res; } if next_defer_list.is_empty() { break; @@ -290,6 +318,7 @@ impl QueryBuilder { env.extensions.execution_end(); let res = QueryResponse { + label: None, path: None, data, extensions: env.extensions.result(), diff --git a/src/subscription/ws_transport.rs b/src/subscription/ws_transport.rs index 62527852..f4dbcf45 100644 --- a/src/subscription/ws_transport.rs +++ b/src/subscription/ws_transport.rs @@ -141,6 +141,7 @@ impl SubscriptionTransport for WebSocketTransport { id: Some(id.clone()), payload: Some( serde_json::to_value(GQLResponse(Ok(QueryResponse { + label: None, path: None, data: value, extensions: None, diff --git a/src/types/deferred.rs b/src/types/deferred.rs index 4e18d28b..331f42cb 100644 --- a/src/types/deferred.rs +++ b/src/types/deferred.rs @@ -76,6 +76,7 @@ impl OutputValueType for Deferred Ok(( QueryResponse { + label: None, path: Some(path_prefix), data, extensions: None, diff --git a/src/types/streamed.rs b/src/types/streamed.rs index 9e2d84ec..7af0c6de 100644 --- a/src/types/streamed.rs +++ b/src/types/streamed.rs @@ -87,6 +87,7 @@ impl OutputValueType for Streamed Ok(( QueryResponse { + label: None, path: Some(path_prefix), data, extensions: None,