Fix relay @defer requires a label in the second chunk. #146

This commit is contained in:
Sunli 2020-06-05 10:18:48 +08:00
parent 0949a022a0
commit 37943fa30a
6 changed files with 48 additions and 5 deletions

View File

@ -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::<String>("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<IntoQueryBuilderOpts>, schema| async move {
|method,
query: String,
content_type,
body,
opts: Arc<IntoQueryBuilderOpts>,
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

View File

@ -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,

View File

@ -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<String>,
/// Path for subsequent response
pub path: Option<Vec<serde_json::Value>>,
@ -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(),

View File

@ -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,

View File

@ -76,6 +76,7 @@ impl<T: OutputValueType + Send + Sync + 'static> OutputValueType for Deferred<T>
Ok((
QueryResponse {
label: None,
path: Some(path_prefix),
data,
extensions: None,

View File

@ -87,6 +87,7 @@ impl<T: OutputValueType + Send + Sync + 'static> OutputValueType for Streamed<T>
Ok((
QueryResponse {
label: None,
path: Some(path_prefix),
data,
extensions: None,