diff --git a/Cargo.lock b/Cargo.lock index 282e55b..09f7d14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,7 @@ checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" name = "apollo-gateway-rs" version = "0.9.0" dependencies = [ - "actix", + "actix",dsx "actix-web", "actix-web-actors", "anyhow", diff --git a/apollo-gateway-rs/src/handler/executor.rs b/apollo-gateway-rs/src/handler/executor.rs index f5a0402..f6e0848 100644 --- a/apollo-gateway-rs/src/handler/executor.rs +++ b/apollo-gateway-rs/src/handler/executor.rs @@ -42,7 +42,7 @@ impl<'e> Executor<'e> { self.resp.into_inner() } RootNode::Subscribe(_) => Response { - data: ConstValue::Null, + data: None, errors: vec![ServerError { message: "Not supported".to_string(), path: Default::default(), @@ -100,7 +100,7 @@ impl<'e> Executor<'e> { id, node.service, RequestData::new(node.query.to_string()) - .variables(node.variables.to_variables()), + .variables(node.variables.to_variables()), tx.clone(), ) .with_context(cx)) @@ -113,7 +113,7 @@ impl<'e> Executor<'e> { vec![KEY_ERROR.string(err.to_string())], ); Response { - data: ConstValue::Null, + data: None, errors: vec![ServerError { message: err.to_string(), path: Default::default(), @@ -193,7 +193,12 @@ impl<'e> Executor<'e> { async fn execute_introspection_node(&self, introspection: &IntrospectionNode) { let value = IntrospectionRoot.resolve(&introspection.selection_set, self.schema); let mut current_resp = self.resp.lock().await; - merge_data(&mut current_resp.data, value); + if current_resp.data .is_none() { + current_resp.data = Some(ConstValue::Null) + } + if let Some(data) = &mut current_resp.data { + merge_data(data, value); + } } async fn execute_fetch_node(&self, fetcher: &impl Fetcher, fetch: &FetchNode<'_>) { @@ -231,7 +236,12 @@ impl<'e> Executor<'e> { if resp.errors.is_empty() { add_tracing_spans(&mut resp); current_resp.headers = resp.headers; - merge_data(&mut current_resp.data, resp.data); + if current_resp.data .is_none() { + current_resp.data = Some(ConstValue::Null) + } + if let Some(data) = &mut current_resp.data { + merge_data(data, resp.data.unwrap_or(ConstValue::Null)); + } } else { rewrite_errors(None, &mut current_resp.errors, resp.errors); } @@ -407,7 +417,7 @@ impl<'e> Executor<'e> { let mut resp = self.resp.lock().await; get_representations( &mut representations, - &mut resp.data, + resp.data.as_mut().unwrap_or(&mut ConstValue::Null), &flatten.path, flatten.prefix, ); @@ -467,10 +477,10 @@ impl<'e> Executor<'e> { Ok(mut resp) => { if resp.errors.is_empty() { add_tracing_spans(&mut resp); - if let ConstValue::Object(mut data) = resp.data { + if let ConstValue::Object(mut data) = resp.data.unwrap_or_default() { if let Some(ConstValue::List(values)) = data.remove("_entities") { flatten_values( - &mut current_resp.data, + current_resp.data.as_mut().unwrap_or(&mut ConstValue::Null), &flatten.path, &mut values.into_iter().fuse(), &mut flags.into_iter().fuse(), diff --git a/apollo-gateway-rs/src/handler/shared_route_table.rs b/apollo-gateway-rs/src/handler/shared_route_table.rs index 75790e3..e07d6ac 100644 --- a/apollo-gateway-rs/src/handler/shared_route_table.rs +++ b/apollo-gateway-rs/src/handler/shared_route_table.rs @@ -11,7 +11,6 @@ use parser::types::{ExecutableDocument, Selection, SelectionSet}; use serde::Deserialize; use tokio::sync::{mpsc, RwLock}; use tokio::time::{Duration, Instant}; -use value::ConstValue; use crate::datasource::RemoteGraphQLDataSource; use crate::GraphqlSourceMiddleware; @@ -116,7 +115,7 @@ impl SharedRouteTable { .await .with_context(|| format!("Failed to fetch SDL from '{}'.", service))?; let resp: ResponseQuery = - value::from_value(resp.data).context("Failed to parse response.")?; + value::from_value(resp.data.unwrap_or_default()).context("Failed to parse response.")?; let document = parser::parse_schema(resp.service.sdl) .with_context(|| format!("Invalid SDL from '{}'.", service))?; Ok::<_, Error>((service.to_string(), document)) @@ -156,7 +155,7 @@ impl SharedRouteTable { Ok(_) => {}, Err(e) => { let response = Response { - data: ConstValue::Null, + data: None, errors: vec![e], extensions: Default::default(), headers: Default::default(), @@ -175,7 +174,7 @@ impl SharedRouteTable { Some((composed_schema, route_table)) => (composed_schema, route_table), _ => { let response = Response { - data: ConstValue::Null, + data: None, errors: vec![ServerError::new("Not ready.")], extensions: Default::default(), headers: Default::default(), diff --git a/apollo-gateway-rs/src/handler/websocket/subscription.rs b/apollo-gateway-rs/src/handler/websocket/subscription.rs index b66765f..566aacb 100644 --- a/apollo-gateway-rs/src/handler/websocket/subscription.rs +++ b/apollo-gateway-rs/src/handler/websocket/subscription.rs @@ -4,7 +4,6 @@ use actix::{Actor, AsyncContext, ActorContext, Handler, StreamHandler}; use crate::schema::ComposedSchema; use actix_web_actors::ws; use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError}; -use value::ConstValue; use crate::planner::{Response, ServerError}; use crate::{RemoteGraphQLDataSource, Context, ServiceRouteTable, GraphqlSourceMiddleware}; use super::protocol::{ClientMessage, ConnectionError, ServerMessage}; @@ -93,7 +92,7 @@ impl StreamHandler document, Err(err) => { let resp = Response { - data: ConstValue::Null, + data: None, errors: vec![ServerError::new(err.to_string())], extensions: Default::default(), headers: Default::default() diff --git a/apollo-gateway-rs/src/planner/builder.rs b/apollo-gateway-rs/src/planner/builder.rs index a03301d..2bbe49e 100644 --- a/apollo-gateway-rs/src/planner/builder.rs +++ b/apollo-gateway-rs/src/planner/builder.rs @@ -62,7 +62,7 @@ impl<'a> PlanBuilder<'a> { crate::validation::check_rules(self.schema, &self.document, &self.variables); if !rule_errors.is_empty() { return Err(Response { - data: ConstValue::Null, + data: None, errors: rule_errors .into_iter() .map(|err| ServerError { diff --git a/apollo-gateway-rs/src/planner/response.rs b/apollo-gateway-rs/src/planner/response.rs index 7435472..674983f 100644 --- a/apollo-gateway-rs/src/planner/response.rs +++ b/apollo-gateway-rs/src/planner/response.rs @@ -38,7 +38,8 @@ impl ServerError { #[derive(Debug, Serialize, Deserialize, Default)] pub struct Response { - pub data: ConstValue, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub data: Option, #[serde(skip_serializing_if = "Vec::is_empty", default)] pub errors: Vec, diff --git a/apollo-gateway-rs/src/validation/utils.rs b/apollo-gateway-rs/src/validation/utils.rs index e80e3c1..4ef183d 100644 --- a/apollo-gateway-rs/src/validation/utils.rs +++ b/apollo-gateway-rs/src/validation/utils.rs @@ -3,7 +3,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use crate::schema::{ComposedSchema, TypeKind}; use parser::types::{BaseType, Type}; -use value::ConstValue; +use value::{ConstValue, Name}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Scope<'a> { @@ -117,6 +117,18 @@ pub fn is_valid_input_value( } else { None } + } else if let ConstValue::String(v) = value { + if ty.enum_values.contains_key(&Name::new(v.to_string())) { + None + } else { + Some(valid_error( + &path_node, + format!( + "enumeration type \"{}\" does not contain the value \"{}\"", + ty.name, value + ) + )) + } } else { Some(valid_error( &path_node,