From ed615379515d3346ced0aa09cb1cd55d7c56925b Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:59:24 +0000 Subject: [PATCH 1/7] fix(generate): generate interface for oneOf --- src/core/config/config.rs | 3 +++ src/core/generator/from_proto.rs | 8 ++++++-- ...nerator__from_proto__test__oneof_types.snap | 16 ++++++++++++---- tests/core/snapshots/grpc-oneof.md_client.snap | 12 ++++++++---- tests/core/snapshots/grpc-oneof.md_merged.snap | 12 ++++++++---- tests/execution/grpc-oneof.md | 18 +++++++++++------- 6 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/core/config/config.rs b/src/core/config/config.rs index cb22208d47..5f15e7fc78 100644 --- a/src/core/config/config.rs +++ b/src/core/config/config.rs @@ -962,6 +962,9 @@ impl Config { stack.extend(field.args.values().map(|arg| arg.type_of.clone())); stack.push(field.type_of.clone()); } + for interface in typ.implements.iter() { + stack.push(interface.clone()) + } } } diff --git a/src/core/generator/from_proto.rs b/src/core/generator/from_proto.rs index b78c837ded..a7bd6ba215 100644 --- a/src/core/generator/from_proto.rs +++ b/src/core/generator/from_proto.rs @@ -127,7 +127,7 @@ impl Context { collect_types( type_name.clone(), - base_type, + base_type.clone(), &oneof_fields, &mut union_types, ); @@ -141,13 +141,17 @@ impl Context { } let mut union_ = Union::default(); + let interface_name = format!("{type_name}__Interface"); - for (type_name, ty) in union_types { + for (type_name, mut ty) in union_types { + ty.implements.insert(interface_name.clone()); union_.types.insert(type_name.clone()); self = self.insert_type(type_name, ty); } + // base interface type + self.config.types.insert(interface_name, base_type); self.config.unions.insert(type_name, union_); self diff --git a/src/core/generator/snapshots/tailcall__core__generator__from_proto__test__oneof_types.snap b/src/core/generator/snapshots/tailcall__core__generator__from_proto__test__oneof_types.snap index df9cc4e60d..098e30b2e2 100644 --- a/src/core/generator/snapshots/tailcall__core__generator__from_proto__test__oneof_types.snap +++ b/src/core/generator/snapshots/tailcall__core__generator__from_proto__test__oneof_types.snap @@ -62,6 +62,14 @@ input oneof__Request__Var__Var1 { usual: String } +interface oneof__Request__Interface { + usual: String +} + +interface oneof__Response__Interface { + usual: Int +} + union oneof__Request = oneof__Request__Var0__Var | oneof__Request__Var0__Var0 | oneof__Request__Var0__Var1 | oneof__Request__Var1__Var | oneof__Request__Var1__Var0 | oneof__Request__Var1__Var1 | oneof__Request__Var__Var | oneof__Request__Var__Var0 | oneof__Request__Var__Var1 union oneof__Response = oneof__Response__Var | oneof__Response__Var0 | oneof__Response__Var1 | oneof__Response__Var2 @@ -78,21 +86,21 @@ type oneof__Payload { payload: String } -type oneof__Response__Var { +type oneof__Response__Var implements oneof__Response__Interface { usual: Int } -type oneof__Response__Var0 { +type oneof__Response__Var0 implements oneof__Response__Interface { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 { +type oneof__Response__Var1 implements oneof__Response__Interface { command: oneof__Command! usual: Int } -type oneof__Response__Var2 { +type oneof__Response__Var2 implements oneof__Response__Interface { response: String! usual: Int } diff --git a/tests/core/snapshots/grpc-oneof.md_client.snap b/tests/core/snapshots/grpc-oneof.md_client.snap index 4b75edbacd..a3bd64ef14 100644 --- a/tests/core/snapshots/grpc-oneof.md_client.snap +++ b/tests/core/snapshots/grpc-oneof.md_client.snap @@ -116,21 +116,25 @@ input oneof__Request__Var__Var1 { union oneof__Response = oneof__Response__Var | oneof__Response__Var0 | oneof__Response__Var1 | oneof__Response__Var2 -type oneof__Response__Var { +interface oneof__Response__Interface { usual: Int } -type oneof__Response__Var0 { +type oneof__Response__Var implements oneof__Response__Interface { + usual: Int +} + +type oneof__Response__Var0 implements oneof__Response__Interface { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 { +type oneof__Response__Var1 implements oneof__Response__Interface { command: oneof__Command! usual: Int } -type oneof__Response__Var2 { +type oneof__Response__Var2 implements oneof__Response__Interface { response: String! usual: Int } diff --git a/tests/core/snapshots/grpc-oneof.md_merged.snap b/tests/core/snapshots/grpc-oneof.md_merged.snap index 2e65b34afa..679c54ba45 100644 --- a/tests/core/snapshots/grpc-oneof.md_merged.snap +++ b/tests/core/snapshots/grpc-oneof.md_merged.snap @@ -65,6 +65,10 @@ input oneof__Request__Var__Var1 { usual: String } +interface oneof__Response__Interface { + usual: Int +} + union oneof__Response = oneof__Response__Var | oneof__Response__Var0 | oneof__Response__Var1 | oneof__Response__Var2 type Query { @@ -96,21 +100,21 @@ type oneof__Payload { payload: String } -type oneof__Response__Var { +type oneof__Response__Var implements oneof__Response__Interface { usual: Int } -type oneof__Response__Var0 { +type oneof__Response__Var0 implements oneof__Response__Interface { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 { +type oneof__Response__Var1 implements oneof__Response__Interface { command: oneof__Command! usual: Int } -type oneof__Response__Var2 { +type oneof__Response__Var2 implements oneof__Response__Interface { response: String! usual: Int } diff --git a/tests/execution/grpc-oneof.md b/tests/execution/grpc-oneof.md index a407cc5a76..1cb3f965ed 100644 --- a/tests/execution/grpc-oneof.md +++ b/tests/execution/grpc-oneof.md @@ -102,6 +102,10 @@ input oneof__Request__Var__Var1 @tag(id: "oneof.Request") { usual: String } +interface oneof__Response__Interface { + usual: Int +} + union oneof__Response = oneof__Response__Var | oneof__Response__Var0 | oneof__Response__Var1 | oneof__Response__Var2 type Query { @@ -133,21 +137,21 @@ type oneof__Payload @tag(id: "oneof.Payload") { payload: String } -type oneof__Response__Var @tag(id: "oneof.Response") { +type oneof__Response__Var implements oneof__Response__Interface { usual: Int } -type oneof__Response__Var0 @tag(id: "oneof.Response") { +type oneof__Response__Var0 implements oneof__Response__Interface { payload: oneof__Payload! usual: Int } -type oneof__Response__Var1 @tag(id: "oneof.Response") { +type oneof__Response__Var1 implements oneof__Response__Interface { command: oneof__Command! usual: Int } -type oneof__Response__Var2 @tag(id: "oneof.Response") { +type oneof__Response__Var2 implements oneof__Response__Interface { response: String! usual: Int } @@ -169,9 +173,9 @@ type oneof__Response__Var2 @tag(id: "oneof.Response") { query: > query { oneof__OneOfService__GetOneOfVar1(request: { command: { command: "start" } }) { - # TODO: check that it's possible to get shared field from Union like that - # outside of the fragment - usual + ... on oneof__Response__Interface { + usual + } ... on oneof__Response__Var1 { command { command From 9014e2942abbabc61fa8c1dbce8a2e68ee3a960b Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:04:28 +0000 Subject: [PATCH 2/7] fix(jit): support select fields on interface --- src/core/blueprint/index.rs | 40 +++++++++++++++++++++++ src/core/ir/resolver_context_like.rs | 3 +- src/core/jit/exec.rs | 34 +++++++++++--------- src/core/jit/model.rs | 47 ++++++++++++++++++---------- src/core/jit/synth/synth.rs | 5 +-- 5 files changed, 93 insertions(+), 36 deletions(-) diff --git a/src/core/blueprint/index.rs b/src/core/blueprint/index.rs index c6e8632a19..f922284977 100644 --- a/src/core/blueprint/index.rs +++ b/src/core/blueprint/index.rs @@ -66,6 +66,37 @@ impl Index { pub fn get_mutation(&self) -> Option<&str> { self.schema.mutation.as_deref() } + + pub fn is_type_implements(&self, type_name: &str, implements: &str) -> bool { + if type_name == implements { + return true; + } + + if !matches!( + self.map.get(implements), + Some((Definition::Interface(_), _)) + ) { + return false; + } + + self.is_type_implements_rec(type_name, implements) + } + + fn is_type_implements_rec(&self, type_name: &str, implements: &str) -> bool { + if type_name == implements { + return true; + } + + if let Some((Definition::Object(obj), _)) = self.map.get(type_name) { + for interface in obj.implements.iter() { + if self.is_type_implements_rec(interface, implements) { + return true; + } + } + } + + false + } } impl From<&Blueprint> for Index { @@ -232,4 +263,13 @@ mod test { index.schema.mutation = None; assert_eq!(index.get_mutation(), None); } + + #[test] + fn test_is_type_implements() { + let index = setup(); + + assert!(index.is_type_implements("User", "Node")); + assert!(index.is_type_implements("Post", "Post")); + assert!(!index.is_type_implements("Node", "User")); + } } diff --git a/src/core/ir/resolver_context_like.rs b/src/core/ir/resolver_context_like.rs index 190f91e5b2..da8cd0508b 100644 --- a/src/core/ir/resolver_context_like.rs +++ b/src/core/ir/resolver_context_like.rs @@ -96,8 +96,9 @@ impl SelectionField { field: &crate::core::jit::Field, ConstValue>, ) -> SelectionField { let name = field.output_name.to_string(); + let type_name = field.type_of.name(); let selection_set = field - .nested_iter(field.type_of.name()) + .nested_iter(|field| field.type_condition == type_name) .map(Self::from_jit_field) .collect(); let args = field diff --git a/src/core/jit/exec.rs b/src/core/jit/exec.rs index aa378f08fd..f8640dff80 100644 --- a/src/core/jit/exec.rs +++ b/src/core/jit/exec.rs @@ -94,13 +94,16 @@ where // Check if the value is an array if let Some(array) = value.as_array() { join_all(array.iter().enumerate().map(|(index, value)| { - let type_name = value.get_type_name().unwrap_or(field.type_of.name()); - - join_all(field.nested_iter(type_name).map(|field| { - let ctx = ctx.with_value_and_field(value, field); - let data_path = data_path.clone().with_index(index); - async move { self.execute(&ctx, data_path).await } - })) + join_all( + self.request + .plan() + .field_nested_iter(field, value) + .map(|field| { + let ctx = ctx.with_value_and_field(value, field); + let data_path = data_path.clone().with_index(index); + async move { self.execute(&ctx, data_path).await } + }), + ) })) .await; } @@ -111,13 +114,16 @@ where // TODO: Validate if the value is an Object // Has to be an Object, we don't do anything while executing if its a Scalar else { - let type_name = value.get_type_name().unwrap_or(field.type_of.name()); - - join_all(field.nested_iter(type_name).map(|child| { - let ctx = ctx.with_value_and_field(value, child); - let data_path = data_path.clone(); - async move { self.execute(&ctx, data_path).await } - })) + join_all( + self.request + .plan() + .field_nested_iter(field, value) + .map(|child| { + let ctx = ctx.with_value_and_field(value, child); + let data_path = data_path.clone(); + async move { self.execute(&ctx, data_path).await } + }), + ) .await; } diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index 2fe51567fa..daa70c528f 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use super::Error; use crate::core::blueprint::Index; use crate::core::ir::model::IR; +use crate::core::ir::TypedValue; #[derive(Debug, Deserialize, Clone)] pub struct Variables(HashMap); @@ -143,6 +144,15 @@ impl Variable { } } +impl Field { + pub fn value_type<'a, Output>(&'a self, value: &'a Output) -> &'a str + where + Output: TypedValue<'a>, + { + value.get_type_name().unwrap_or(self.type_of.name()) + } +} + impl Field, Input> { pub fn try_map( self, @@ -215,27 +225,15 @@ impl Field { } impl Field, Input> { - /// iters over children fields that are - /// related to passed `type_name` either - /// as direct field of the queried type or - /// field from fragment on type `type_name` + /// iters over children fields that satisfies + /// passed filter_fn pub fn nested_iter<'a>( &'a self, - type_name: &'a str, + mut filter_fn: impl FnMut(&'a Field, Input>) -> bool + 'a, ) -> impl Iterator, Input>> + 'a { self.extensions .as_ref() - .map(move |nested| { - nested - .0 - .iter() - // TODO: handle Interface and Union types here - // Right now only exact type name is used to check the set of fields - // but with Interfaces/Unions we need to check if that specific type - // is member of some Interface/Union and if so call the fragments for - // the related Interfaces/Unions - .filter(move |field| field.type_condition == type_name) - }) + .map(move |nested| nested.0.iter().filter(move |&field| filter_fn(field))) .into_iter() .flatten() } @@ -332,7 +330,6 @@ pub struct OperationPlan { flat: Vec>, operation_type: OperationType, nested: Vec, Input>>, - // TODO: drop index from here. Embed all the necessary information in each field of the plan. pub index: Arc, } @@ -447,6 +444,22 @@ impl OperationPlan { ) -> bool { self.index.validate_enum_value(field.type_of.name(), value) } + + pub fn field_nested_iter<'a, Output>( + &'a self, + field: &'a Field, Input>, + value: &'a Output, + ) -> impl Iterator, Input>> + where + Output: TypedValue<'a>, + { + let value_type = field.value_type(value); + + field.nested_iter(move |field| { + self.index + .is_type_implements(value_type, &field.type_condition) + }) + } } #[derive(Clone, Debug)] diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index bcc0fb13e0..0f2a220631 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -1,4 +1,3 @@ -use crate::core::ir::TypedValue; use crate::core::jit::model::{Field, Nested, OperationPlan, Variable, Variables}; use crate::core::jit::store::{Data, DataPath, Store}; use crate::core::jit::{Error, PathSegment, Positioned, ValidationError}; @@ -166,9 +165,7 @@ where (_, Some(obj)) => { let mut ans = Value::JsonObject::new(); - let type_name = value.get_type_name().unwrap_or(node.type_of.name()); - - for child in node.nested_iter(type_name) { + for child in self.plan.field_nested_iter(node, value) { // all checks for skip must occur in `iter_inner` // and include be checked before calling `iter` or recursing. let include = self.include(child); From 88462a083532cbd783204a0dd814ea03ba30bc0b Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Wed, 28 Aug 2024 07:59:55 +0000 Subject: [PATCH 3/7] add comments and renaming --- src/core/ir/resolver_context_like.rs | 2 +- src/core/jit/exec.rs | 4 ++-- src/core/jit/model.rs | 19 ++++++++++++++++--- src/core/jit/synth/synth.rs | 2 +- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/core/ir/resolver_context_like.rs b/src/core/ir/resolver_context_like.rs index da8cd0508b..e54e6f653e 100644 --- a/src/core/ir/resolver_context_like.rs +++ b/src/core/ir/resolver_context_like.rs @@ -98,7 +98,7 @@ impl SelectionField { let name = field.output_name.to_string(); let type_name = field.type_of.name(); let selection_set = field - .nested_iter(|field| field.type_condition == type_name) + .iter_only(|field| field.type_condition == type_name) .map(Self::from_jit_field) .collect(); let args = field diff --git a/src/core/jit/exec.rs b/src/core/jit/exec.rs index f8640dff80..ee9a890bb2 100644 --- a/src/core/jit/exec.rs +++ b/src/core/jit/exec.rs @@ -97,7 +97,7 @@ where join_all( self.request .plan() - .field_nested_iter(field, value) + .field_iter_only(field, value) .map(|field| { let ctx = ctx.with_value_and_field(value, field); let data_path = data_path.clone().with_index(index); @@ -117,7 +117,7 @@ where join_all( self.request .plan() - .field_nested_iter(field, value) + .field_iter_only(field, value) .map(|child| { let ctx = ctx.with_value_and_field(value, child); let data_path = data_path.clone(); diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index d7c6392328..05d4c62aef 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -164,6 +164,7 @@ impl Variable { } impl Field { + /// Returns the __typename of the value related to this field pub fn value_type<'a, Output>(&'a self, value: &'a Output) -> &'a str where Output: TypedValue<'a>, @@ -246,7 +247,7 @@ impl Field { impl Field, Input> { /// iters over children fields that satisfies /// passed filter_fn - pub fn nested_iter<'a>( + pub fn iter_only<'a>( &'a self, mut filter_fn: impl FnMut(&'a Field, Input>) -> bool + 'a, ) -> impl Iterator, Input>> + 'a { @@ -406,30 +407,37 @@ impl OperationPlan { Self { flat: fields, nested, operation_type, index } } + /// Returns a graphQL operation type pub fn operation_type(&self) -> OperationType { self.operation_type } + /// Check if current graphQL operation is query pub fn is_query(&self) -> bool { self.operation_type == OperationType::Query } + /// Returns a nested [Field] representation pub fn as_nested(&self) -> &[Field, Input>] { &self.nested } + /// Returns an owned version of [Field] representation pub fn into_nested(self) -> Vec, Input>> { self.nested } + /// Returns a flat [Field] representation pub fn as_parent(&self) -> &[Field] { &self.flat } + /// Search for a field with a specified [FieldId] pub fn find_field(&self, id: FieldId) -> Option<&Field> { self.flat.iter().find(|field| field.id == id) } + /// Search for a field by specified path of nested fields pub fn find_field_path>(&self, path: &[S]) -> Option<&Field> { match path.split_first() { None => None, @@ -444,18 +452,22 @@ impl OperationPlan { } } + /// Returns number of fields in plan pub fn size(&self) -> usize { self.flat.len() } + /// Check if the field is of scalar type pub fn field_is_scalar(&self, field: &Field) -> bool { self.index.type_is_scalar(field.type_of.name()) } + /// Check if the field is of enum type pub fn field_is_enum(&self, field: &Field) -> bool { self.index.type_is_enum(field.type_of.name()) } + /// Validate the value against enum variants of the field pub fn field_validate_enum_value( &self, field: &Field, @@ -464,7 +476,8 @@ impl OperationPlan { self.index.validate_enum_value(field.type_of.name(), value) } - pub fn field_nested_iter<'a, Output>( + /// Iterate over nested fields that are related to the __typename of the value + pub fn field_iter_only<'a, Output>( &'a self, field: &'a Field, Input>, value: &'a Output, @@ -474,7 +487,7 @@ impl OperationPlan { { let value_type = field.value_type(value); - field.nested_iter(move |field| { + field.iter_only(move |field| { self.index .is_type_implements(value_type, &field.type_condition) }) diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index 1e542e5be6..e05e9b9ff6 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -163,7 +163,7 @@ where (_, Some(obj)) => { let mut ans = Value::JsonObject::new(); - for child in self.plan.field_nested_iter(node, value) { + for child in self.plan.field_iter_only(node, value) { // all checks for skip must occur in `iter_inner` // and include be checked before calling `iter` or recursing. let include = self.include(child); From 2d00367929f232ff2539a6eea878d57c148bf5c4 Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:21:39 +0000 Subject: [PATCH 4/7] remove recursion --- src/core/blueprint/index.rs | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/src/core/blueprint/index.rs b/src/core/blueprint/index.rs index f922284977..d8ebe1a317 100644 --- a/src/core/blueprint/index.rs +++ b/src/core/blueprint/index.rs @@ -67,35 +67,16 @@ impl Index { self.schema.mutation.as_deref() } - pub fn is_type_implements(&self, type_name: &str, implements: &str) -> bool { - if type_name == implements { - return true; - } - - if !matches!( - self.map.get(implements), - Some((Definition::Interface(_), _)) - ) { - return false; - } - - self.is_type_implements_rec(type_name, implements) - } - - fn is_type_implements_rec(&self, type_name: &str, implements: &str) -> bool { - if type_name == implements { + pub fn is_type_implements(&self, type_name: &str, type_or_interface: &str) -> bool { + if type_name == type_or_interface { return true; } if let Some((Definition::Object(obj), _)) = self.map.get(type_name) { - for interface in obj.implements.iter() { - if self.is_type_implements_rec(interface, implements) { - return true; - } - } + obj.implements.contains(type_or_interface) + } else { + false } - - false } } From c23a0b91507dda1b7c88a2109a8a31bad85a37af Mon Sep 17 00:00:00 2001 From: meskill <8974488+meskill@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:44:05 +0000 Subject: [PATCH 5/7] fix lint --- src/core/jit/model.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index 05d4c62aef..a6ba7e61d2 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize}; use super::Error; use crate::core::blueprint::Index; use crate::core::ir::model::IR; -use crate::core::json::JsonLike; use crate::core::ir::TypedValue; +use crate::core::json::JsonLike; #[derive(Debug, Deserialize, Clone)] pub struct Variables(HashMap); @@ -476,7 +476,8 @@ impl OperationPlan { self.index.validate_enum_value(field.type_of.name(), value) } - /// Iterate over nested fields that are related to the __typename of the value + /// Iterate over nested fields that are related to the __typename of the + /// value pub fn field_iter_only<'a, Output>( &'a self, field: &'a Field, Input>, From 16c26f3db60652b59761fb8f914ac16c6f2d1360 Mon Sep 17 00:00:00 2001 From: Tushar Mathur Date: Wed, 28 Aug 2024 20:52:51 +0530 Subject: [PATCH 6/7] update model --- src/core/jit/model.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index a6ba7e61d2..ed45eda15d 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -66,6 +66,14 @@ impl Field { skip == include } + + /// Returns the __typename of the value related to this field + pub fn value_type<'a, Output>(&'a self, value: &'a Output) -> &'a str + where + Output: TypedValue<'a>, + { + value.get_type_name().unwrap_or(self.type_of.name()) + } } #[derive(Debug, Clone)] @@ -163,16 +171,6 @@ impl Variable { } } -impl Field { - /// Returns the __typename of the value related to this field - pub fn value_type<'a, Output>(&'a self, value: &'a Output) -> &'a str - where - Output: TypedValue<'a>, - { - value.get_type_name().unwrap_or(self.type_of.name()) - } -} - impl Field, Input> { pub fn try_map( self, From be55958298753be87edd2faebd02a86a0c59eefc Mon Sep 17 00:00:00 2001 From: Kiryl Mialeshka <8974488+meskill@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:55:29 +0200 Subject: [PATCH 7/7] fix(jit): resolve __typename for values (#2766) --- src/core/ir/resolver_context_like.rs | 5 +- src/core/jit/builder.rs | 6 +- src/core/jit/model.rs | 9 +- ...ore__jit__builder__tests__alias_query.snap | 12 +- ...e__jit__builder__tests__default_value.snap | 8 +- ...core__jit__builder__tests__directives.snap | 12 +- ..._core__jit__builder__tests__fragments.snap | 24 +- ...e__jit__builder__tests__from_document.snap | 16 +- ...__builder__tests__multiple_operations.snap | 24 +- ...builder__tests__resolving_operation-2.snap | 20 +- ...__builder__tests__resolving_operation.snap | 16 +- ..._jit__builder__tests__simple_mutation.snap | 28 +- ...re__jit__builder__tests__simple_query.snap | 12 +- ...ll__core__jit__builder__tests__unions.snap | 12 +- ..._core__jit__builder__tests__variables.snap | 12 +- ...nth__tests__json_placeholder_typename.snap | 808 ++++++++++++++++++ src/core/jit/synth/synth.rs | 17 +- 17 files changed, 983 insertions(+), 58 deletions(-) create mode 100644 src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename.snap diff --git a/src/core/ir/resolver_context_like.rs b/src/core/ir/resolver_context_like.rs index e54e6f653e..93dfaaf1a1 100644 --- a/src/core/ir/resolver_context_like.rs +++ b/src/core/ir/resolver_context_like.rs @@ -98,7 +98,10 @@ impl SelectionField { let name = field.output_name.to_string(); let type_name = field.type_of.name(); let selection_set = field - .iter_only(|field| field.type_condition == type_name) + .iter_only(|field| match &field.type_condition { + Some(type_condition) => type_condition == type_name, + None => true, + }) .map(Self::from_jit_field) .collect(); let args = field diff --git a/src/core/jit/builder.rs b/src/core/jit/builder.rs index 0d9a0c88ea..7e662d9c03 100644 --- a/src/core/jit/builder.rs +++ b/src/core/jit/builder.rs @@ -220,7 +220,7 @@ impl Builder { .unwrap_or(field_name.to_owned()), ir, type_of, - type_condition: type_condition.to_string(), + type_condition: Some(type_condition.to_string()), skip, include, args, @@ -241,7 +241,9 @@ impl Builder { name: "String".to_owned(), non_null: true, }, - type_condition: type_condition.to_string(), + // __typename has a special meaning and could be applied + // to any type + type_condition: None, skip, include, args: Vec::new(), diff --git a/src/core/jit/model.rs b/src/core/jit/model.rs index ed45eda15d..0bf448dac6 100644 --- a/src/core/jit/model.rs +++ b/src/core/jit/model.rs @@ -147,7 +147,7 @@ pub struct Field { /// The type could be anything from graphql type system: /// interface, type, union, input type. /// See [spec](https://spec.graphql.org/October2021/#sec-Type-Conditions) - pub type_condition: String, + pub type_condition: Option, pub skip: Option, pub include: Option, pub args: Vec>, @@ -486,9 +486,10 @@ impl OperationPlan { { let value_type = field.value_type(value); - field.iter_only(move |field| { - self.index - .is_type_implements(value_type, &field.type_condition) + field.iter_only(move |field| match &field.type_condition { + Some(type_condition) => self.index.is_type_implements(value_type, type_condition), + // if there is no type_condition restriction then use this field + None => true, }) } } diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap index 0e0d13d8e2..70ebd89e69 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__alias_query.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "articles", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -19,7 +21,9 @@ expression: plan.into_nested() output_name: "author", ir: "Some(..)", type_of: User, - type_condition: "Post", + type_condition: Some( + "Post", + ), extensions: Some( Nested( [ @@ -28,7 +32,9 @@ expression: plan.into_nested() name: "id", output_name: "identifier", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap index b9ce57c2f4..2ab1e80e91 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__default_value.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "createPost", ir: "Some(..)", type_of: Post, - type_condition: "Mutation", + type_condition: Some( + "Mutation", + ), args: [ Arg { id: 0, @@ -47,7 +49,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap index a93aafa00d..b35eadcd1d 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__directives.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "users", ir: "Some(..)", type_of: [User], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -18,7 +20,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [ Directive { name: "options", @@ -38,7 +42,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), include: Some( Variable( "includeName", diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap index 40266d97e5..b8201fa695 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__fragments.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Query", + type_condition: Some( + "Query", + ), args: [ Arg { id: 0, @@ -31,7 +33,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -39,7 +43,9 @@ expression: plan.into_nested() name: "email", output_name: "email", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -47,7 +53,9 @@ expression: plan.into_nested() name: "phone", output_name: "phone", type_of: String, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -55,7 +63,9 @@ expression: plan.into_nested() name: "title", output_name: "title", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -63,7 +73,9 @@ expression: plan.into_nested() name: "body", output_name: "body", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap index c1e6d168b7..a66c07dc6e 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__from_document.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "posts", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -19,7 +21,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Post", + type_condition: Some( + "Post", + ), extensions: Some( Nested( [ @@ -28,7 +32,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -36,7 +42,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap index 31fc82f0ba..999533bf3e 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__multiple_operations.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Query", + type_condition: Some( + "Query", + ), args: [ Arg { id: 0, @@ -31,7 +33,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -39,7 +43,9 @@ expression: plan.into_nested() name: "username", output_name: "username", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], @@ -53,7 +59,9 @@ expression: plan.into_nested() output_name: "posts", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -62,7 +70,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -70,7 +80,9 @@ expression: plan.into_nested() name: "title", output_name: "title", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap index 84141b1a62..1ef844578d 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation-2.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "createPost", ir: "Some(..)", type_of: Post, - type_condition: "Mutation", + type_condition: Some( + "Mutation", + ), args: [ Arg { id: 0, @@ -47,7 +49,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -55,7 +59,9 @@ expression: plan.into_nested() name: "userId", output_name: "userId", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -63,7 +69,9 @@ expression: plan.into_nested() name: "title", output_name: "title", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -71,7 +79,9 @@ expression: plan.into_nested() name: "body", output_name: "body", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap index 9a433b4292..8c61134907 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__resolving_operation.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "posts", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -18,7 +20,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -26,7 +30,9 @@ expression: plan.into_nested() name: "userId", output_name: "userId", type_of: ID!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, Field { @@ -34,7 +40,9 @@ expression: plan.into_nested() name: "title", output_name: "title", type_of: String!, - type_condition: "Post", + type_condition: Some( + "Post", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap index d447afdc34..242fca4dfd 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_mutation.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "createUser", ir: "Some(..)", type_of: User, - type_condition: "Mutation", + type_condition: Some( + "Mutation", + ), args: [ Arg { id: 0, @@ -62,7 +64,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -70,7 +74,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -78,7 +84,9 @@ expression: plan.into_nested() name: "email", output_name: "email", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -86,7 +94,9 @@ expression: plan.into_nested() name: "phone", output_name: "phone", type_of: String, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -94,7 +104,9 @@ expression: plan.into_nested() name: "website", output_name: "website", type_of: String, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -102,7 +114,9 @@ expression: plan.into_nested() name: "username", output_name: "username", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap index 305af5ecbe..b4109ee4ac 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__simple_query.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "posts", ir: "Some(..)", type_of: [Post], - type_condition: "Query", + type_condition: Some( + "Query", + ), extensions: Some( Nested( [ @@ -19,7 +21,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Post", + type_condition: Some( + "Post", + ), extensions: Some( Nested( [ @@ -28,7 +32,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap index 5d1a5b0b6f..46b9485a94 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__unions.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "getUserIdOrEmail", ir: "Some(..)", type_of: UserIdOrEmail, - type_condition: "Query", + type_condition: Some( + "Query", + ), args: [ Arg { id: 0, @@ -31,7 +33,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "UserId", + type_condition: Some( + "UserId", + ), directives: [], }, Field { @@ -39,7 +43,9 @@ expression: plan.into_nested() name: "email", output_name: "email", type_of: String!, - type_condition: "UserEmail", + type_condition: Some( + "UserEmail", + ), directives: [], }, ], diff --git a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap index 0c7d65e877..0c29d55b1f 100644 --- a/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap +++ b/src/core/jit/snapshots/tailcall__core__jit__builder__tests__variables.snap @@ -9,7 +9,9 @@ expression: plan.into_nested() output_name: "user", ir: "Some(..)", type_of: User, - type_condition: "Query", + type_condition: Some( + "Query", + ), args: [ Arg { id: 0, @@ -31,7 +33,9 @@ expression: plan.into_nested() name: "id", output_name: "id", type_of: ID!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, Field { @@ -39,7 +43,9 @@ expression: plan.into_nested() name: "name", output_name: "name", type_of: String!, - type_condition: "User", + type_condition: Some( + "User", + ), directives: [], }, ], diff --git a/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename.snap b/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename.snap new file mode 100644 index 0000000000..c00b1c6293 --- /dev/null +++ b/src/core/jit/synth/snapshots/tailcall__core__jit__synth__synth__tests__json_placeholder_typename.snap @@ -0,0 +1,808 @@ +--- +source: src/core/jit/synth/synth.rs +expression: "serde_json::to_string_pretty(&val).unwrap()" +--- +{ + "posts": [ + { + "id": 1, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 2, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 3, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 4, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 5, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 6, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 7, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 8, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 9, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 10, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 1 + } + }, + { + "id": 11, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 12, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 13, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 14, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 15, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 16, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 17, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 18, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 19, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 20, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 2 + } + }, + { + "id": 21, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 22, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 23, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 24, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 25, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 26, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 27, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 28, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 29, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 30, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 3 + } + }, + { + "id": 31, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 32, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 33, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 34, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 35, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 36, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 37, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 38, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 39, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 40, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 4 + } + }, + { + "id": 41, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 42, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 43, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 44, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 45, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 46, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 47, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 48, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 49, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 50, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 5 + } + }, + { + "id": 51, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 52, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 53, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 54, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 55, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 56, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 57, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 58, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 59, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 60, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 6 + } + }, + { + "id": 61, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 62, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 63, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 64, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 65, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 66, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 67, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 68, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 69, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 70, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 7 + } + }, + { + "id": 71, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 72, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 73, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 74, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 75, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 76, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 77, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 78, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 79, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 80, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 8 + } + }, + { + "id": 81, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 82, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 83, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 84, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 85, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 86, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 87, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 88, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 89, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 90, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 9 + } + }, + { + "id": 91, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 92, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 93, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 94, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 95, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 96, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 97, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 98, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 99, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + }, + { + "id": 100, + "__typename": "Post", + "user": { + "__typename": "User", + "id": 10 + } + } + ] +} diff --git a/src/core/jit/synth/synth.rs b/src/core/jit/synth/synth.rs index e05e9b9ff6..0161de44a8 100644 --- a/src/core/jit/synth/synth.rs +++ b/src/core/jit/synth/synth.rs @@ -168,8 +168,13 @@ where // and include be checked before calling `iter` or recursing. let include = self.include(child); if include { - let val = obj.get_key(child.name.as_str()); - ans.insert_key(&child.output_name, self.iter(child, val, data_path)?); + let value = if child.name == "__typename" { + Value::string(node.value_type(value).into()) + } else { + let val = obj.get_key(child.name.as_str()); + self.iter(child, val, data_path)? + }; + ans.insert_key(&child.output_name, value); } } @@ -421,4 +426,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() { + let jp = JP::init("{ posts { id __typename user { __typename 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()) + } }