diff --git a/src/core/jit/builder.rs b/src/core/jit/builder.rs index 970eef92fb..d7d9a2e080 100644 --- a/src/core/jit/builder.rs +++ b/src/core/jit/builder.rs @@ -354,6 +354,7 @@ impl Builder { }); let plan = OperationPlan::new( + name, fields, operation.ty, self.index.clone(), diff --git a/src/core/jit/input_resolver.rs b/src/core/jit/input_resolver.rs index fcde3c387b..31bc8b498b 100644 --- a/src/core/jit/input_resolver.rs +++ b/src/core/jit/input_resolver.rs @@ -82,6 +82,7 @@ where .collect::, _>>()?; Ok(OperationPlan::new( + self.plan.root_name(), new_fields, self.plan.operation_type(), self.plan.index.clone(), diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index 2936676cb8..dbec574db6 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -345,6 +345,7 @@ pub struct Nested(Vec, Input>>); #[derive(Clone)] pub struct OperationPlan { + root_name: String, flat: Vec>, operation_type: OperationType, nested: Vec, Input>>, @@ -379,6 +380,7 @@ impl OperationPlan { } Ok(OperationPlan { + root_name: self.root_name, flat, operation_type: self.operation_type, nested, @@ -389,7 +391,9 @@ impl OperationPlan { } impl OperationPlan { + #[allow(clippy::too_many_arguments)] pub fn new( + root_name: &str, fields: Vec>, operation_type: OperationType, index: Arc, @@ -406,6 +410,7 @@ impl OperationPlan { .collect::>(); Self { + root_name: root_name.to_string(), flat: fields, nested, operation_type, @@ -414,6 +419,11 @@ impl OperationPlan { } } + /// Returns the name of the root type + pub fn root_name(&self) -> &str { + &self.root_name + } + /// Returns a graphQL operation type pub fn operation_type(&self) -> OperationType { self.operation_type diff --git a/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename_root_level.snap b/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename_root_level.snap new file mode 100644 index 0000000000..a48b858ce3 --- /dev/null +++ b/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename_root_level.snap @@ -0,0 +1,609 @@ +--- +source: src/core/jit/synth/synth.rs +expression: "serde_json::to_string_pretty(&val).unwrap()" +--- +{ + "__typename": "Query", + "posts": [ + { + "id": 1, + "user": { + "id": 1 + } + }, + { + "id": 2, + "user": { + "id": 1 + } + }, + { + "id": 3, + "user": { + "id": 1 + } + }, + { + "id": 4, + "user": { + "id": 1 + } + }, + { + "id": 5, + "user": { + "id": 1 + } + }, + { + "id": 6, + "user": { + "id": 1 + } + }, + { + "id": 7, + "user": { + "id": 1 + } + }, + { + "id": 8, + "user": { + "id": 1 + } + }, + { + "id": 9, + "user": { + "id": 1 + } + }, + { + "id": 10, + "user": { + "id": 1 + } + }, + { + "id": 11, + "user": { + "id": 2 + } + }, + { + "id": 12, + "user": { + "id": 2 + } + }, + { + "id": 13, + "user": { + "id": 2 + } + }, + { + "id": 14, + "user": { + "id": 2 + } + }, + { + "id": 15, + "user": { + "id": 2 + } + }, + { + "id": 16, + "user": { + "id": 2 + } + }, + { + "id": 17, + "user": { + "id": 2 + } + }, + { + "id": 18, + "user": { + "id": 2 + } + }, + { + "id": 19, + "user": { + "id": 2 + } + }, + { + "id": 20, + "user": { + "id": 2 + } + }, + { + "id": 21, + "user": { + "id": 3 + } + }, + { + "id": 22, + "user": { + "id": 3 + } + }, + { + "id": 23, + "user": { + "id": 3 + } + }, + { + "id": 24, + "user": { + "id": 3 + } + }, + { + "id": 25, + "user": { + "id": 3 + } + }, + { + "id": 26, + "user": { + "id": 3 + } + }, + { + "id": 27, + "user": { + "id": 3 + } + }, + { + "id": 28, + "user": { + "id": 3 + } + }, + { + "id": 29, + "user": { + "id": 3 + } + }, + { + "id": 30, + "user": { + "id": 3 + } + }, + { + "id": 31, + "user": { + "id": 4 + } + }, + { + "id": 32, + "user": { + "id": 4 + } + }, + { + "id": 33, + "user": { + "id": 4 + } + }, + { + "id": 34, + "user": { + "id": 4 + } + }, + { + "id": 35, + "user": { + "id": 4 + } + }, + { + "id": 36, + "user": { + "id": 4 + } + }, + { + "id": 37, + "user": { + "id": 4 + } + }, + { + "id": 38, + "user": { + "id": 4 + } + }, + { + "id": 39, + "user": { + "id": 4 + } + }, + { + "id": 40, + "user": { + "id": 4 + } + }, + { + "id": 41, + "user": { + "id": 5 + } + }, + { + "id": 42, + "user": { + "id": 5 + } + }, + { + "id": 43, + "user": { + "id": 5 + } + }, + { + "id": 44, + "user": { + "id": 5 + } + }, + { + "id": 45, + "user": { + "id": 5 + } + }, + { + "id": 46, + "user": { + "id": 5 + } + }, + { + "id": 47, + "user": { + "id": 5 + } + }, + { + "id": 48, + "user": { + "id": 5 + } + }, + { + "id": 49, + "user": { + "id": 5 + } + }, + { + "id": 50, + "user": { + "id": 5 + } + }, + { + "id": 51, + "user": { + "id": 6 + } + }, + { + "id": 52, + "user": { + "id": 6 + } + }, + { + "id": 53, + "user": { + "id": 6 + } + }, + { + "id": 54, + "user": { + "id": 6 + } + }, + { + "id": 55, + "user": { + "id": 6 + } + }, + { + "id": 56, + "user": { + "id": 6 + } + }, + { + "id": 57, + "user": { + "id": 6 + } + }, + { + "id": 58, + "user": { + "id": 6 + } + }, + { + "id": 59, + "user": { + "id": 6 + } + }, + { + "id": 60, + "user": { + "id": 6 + } + }, + { + "id": 61, + "user": { + "id": 7 + } + }, + { + "id": 62, + "user": { + "id": 7 + } + }, + { + "id": 63, + "user": { + "id": 7 + } + }, + { + "id": 64, + "user": { + "id": 7 + } + }, + { + "id": 65, + "user": { + "id": 7 + } + }, + { + "id": 66, + "user": { + "id": 7 + } + }, + { + "id": 67, + "user": { + "id": 7 + } + }, + { + "id": 68, + "user": { + "id": 7 + } + }, + { + "id": 69, + "user": { + "id": 7 + } + }, + { + "id": 70, + "user": { + "id": 7 + } + }, + { + "id": 71, + "user": { + "id": 8 + } + }, + { + "id": 72, + "user": { + "id": 8 + } + }, + { + "id": 73, + "user": { + "id": 8 + } + }, + { + "id": 74, + "user": { + "id": 8 + } + }, + { + "id": 75, + "user": { + "id": 8 + } + }, + { + "id": 76, + "user": { + "id": 8 + } + }, + { + "id": 77, + "user": { + "id": 8 + } + }, + { + "id": 78, + "user": { + "id": 8 + } + }, + { + "id": 79, + "user": { + "id": 8 + } + }, + { + "id": 80, + "user": { + "id": 8 + } + }, + { + "id": 81, + "user": { + "id": 9 + } + }, + { + "id": 82, + "user": { + "id": 9 + } + }, + { + "id": 83, + "user": { + "id": 9 + } + }, + { + "id": 84, + "user": { + "id": 9 + } + }, + { + "id": 85, + "user": { + "id": 9 + } + }, + { + "id": 86, + "user": { + "id": 9 + } + }, + { + "id": 87, + "user": { + "id": 9 + } + }, + { + "id": 88, + "user": { + "id": 9 + } + }, + { + "id": 89, + "user": { + "id": 9 + } + }, + { + "id": 90, + "user": { + "id": 9 + } + }, + { + "id": 91, + "user": { + "id": 10 + } + }, + { + "id": 92, + "user": { + "id": 10 + } + }, + { + "id": 93, + "user": { + "id": 10 + } + }, + { + "id": 94, + "user": { + "id": 10 + } + }, + { + "id": 95, + "user": { + "id": 10 + } + }, + { + "id": 96, + "user": { + "id": 10 + } + }, + { + "id": 97, + "user": { + "id": 10 + } + }, + { + "id": 98, + "user": { + "id": 10 + } + }, + { + "id": 99, + "user": { + "id": 10 + } + }, + { + "id": 100, + "user": { + "id": 10 + } + } + ] +} diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index 4a037c363b..d02ed6ebc5 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use crate::core::jit::model::{Field, Nested, OperationPlan, Variables}; use crate::core::jit::store::{DataPath, Store}; use crate::core::jit::{Error, PathSegment, Positioned, ValidationError}; @@ -36,6 +38,7 @@ where pub fn synthesize(&'a self) -> Result> { let mut data = Value::JsonObject::new(); let mut path = Vec::new(); + let root_name = self.plan.root_name(); for child in self.plan.as_nested().iter() { if !self.include(child) { @@ -43,7 +46,7 @@ where } // TODO: in case of error set `child.output_name` to null // and append error to response error array - let val = self.iter(child, None, &DataPath::new(), &mut path)?; + let val = self.iter(child, None, &DataPath::new(), &mut path, Some(root_name))?; data.insert_key(&child.output_name, val); } @@ -58,6 +61,7 @@ where value: Option<&'a Value>, data_path: &DataPath, path: &mut Vec, + root_name: Option<&'a str>, ) -> Result> { path.push(PathSegment::Field(node.output_name.clone())); @@ -74,13 +78,13 @@ where } if node.type_of.is_list() != value.as_array().is_some() { - return self.node_nullable_guard(node, path); + return self.node_nullable_guard(node, path, None); } self.iter_inner(node, value, data_path, path) } None => match value { Some(result) => self.iter_inner(node, result, data_path, path), - None => self.node_nullable_guard(node, path), + None => self.node_nullable_guard(node, path, root_name), }, }; @@ -94,7 +98,13 @@ where &'a self, node: &'a Field, Value>, path: &[PathSegment], + root_name: Option<&'a str>, ) -> Result> { + if let Some(root_name) = root_name { + if node.name.eq("__typename") { + return Ok(Value::string(Cow::Borrowed(root_name))); + } + } // according to GraphQL spec https://spec.graphql.org/October2021/#sec-Handling-Field-Errors if node.type_of.is_nullable() { Ok(Value::null()) @@ -173,7 +183,7 @@ where Value::string(node.value_type(value).into()) } else { let val = obj.get_key(child.name.as_str()); - self.iter(child, val, data_path, path)? + self.iter(child, val, data_path, path, None)? }; ans.insert_key(&child.output_name, value); } @@ -418,4 +428,12 @@ mod tests { let val: serde_json_borrow::Value = synth.synthesize().unwrap(); insta::assert_snapshot!(serde_json::to_string_pretty(&val).unwrap()) } + + #[test] + fn test_json_placeholder_typename_root_level() { + let jp = JP::init("{ __typename posts { id user { id }} }", None); + let synth = jp.synth(); + let val: serde_json_borrow::Value = synth.synthesize().unwrap(); + insta::assert_snapshot!(serde_json::to_string_pretty(&val).unwrap()) + } }