diff --git a/src/core/blueprint/index.rs b/src/core/blueprint/index.rs index c6e8632a19..d8ebe1a317 100644 --- a/src/core/blueprint/index.rs +++ b/src/core/blueprint/index.rs @@ -66,6 +66,18 @@ impl Index { pub fn get_mutation(&self) -> Option<&str> { self.schema.mutation.as_deref() } + + 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) { + obj.implements.contains(type_or_interface) + } else { + false + } + } } impl From<&Blueprint> for Index { @@ -232,4 +244,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/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/src/core/ir/resolver_context_like.rs b/src/core/ir/resolver_context_like.rs index 190f91e5b2..93dfaaf1a1 100644 --- a/src/core/ir/resolver_context_like.rs +++ b/src/core/ir/resolver_context_like.rs @@ -96,8 +96,12 @@ 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()) + .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/exec.rs b/src/core/jit/exec.rs index aa378f08fd..ee9a890bb2 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_iter_only(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_iter_only(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 bcee777ce4..0bf448dac6 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; use crate::core::json::JsonLike; #[derive(Debug, Deserialize, Clone)] @@ -65,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)] @@ -138,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>, @@ -234,27 +243,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` - pub fn nested_iter<'a>( + /// iters over children fields that satisfies + /// passed filter_fn + pub fn iter_only<'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() } @@ -351,7 +348,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, } @@ -409,30 +405,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, @@ -447,18 +450,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, @@ -466,6 +473,25 @@ impl OperationPlan { ) -> bool { self.index.validate_enum_value(field.type_of.name(), 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>, + value: &'a Output, + ) -> impl Iterator, Input>> + where + Output: TypedValue<'a>, + { + let value_type = field.value_type(value); + + 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, + }) + } } #[derive(Clone, Debug)] 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 387952bb30..0161de44a8 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, Variables}; use crate::core::jit::store::{Data, DataPath, Store}; use crate::core::jit::{Error, PathSegment, Positioned, ValidationError}; @@ -164,15 +163,18 @@ 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_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); 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); } } @@ -424,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()) + } } 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 eb5857799e..3b23f10b19 100644 --- a/tests/execution/grpc-oneof.md +++ b/tests/execution/grpc-oneof.md @@ -102,6 +102,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 { @@ -133,21 +137,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 } @@ -169,9 +173,9 @@ type oneof__Response__Var2 { 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