From d0e9202f415b24b05d5b0e9b5be444503480ebbb Mon Sep 17 00:00:00 2001
From: Magnus Hallin <mhallin@fastmail.com>
Date: Mon, 20 Nov 2017 08:53:13 +0100
Subject: [PATCH] Add tracking of the current type being resolved in the
 Executor

This is not 100% accurate - it will be set to the literal type of what
a field returns, so it might be wrapped in non-null/list when it
technically shouldn't.

For this use-case it's fine, but if we want to (officially) add it to
the public API surface, we should probably make it accurate.
---
 juniper/src/executor.rs     | 49 ++++++++++++++++++++++++++++++++-----
 juniper/src/schema/model.rs |  1 +
 juniper/src/types/base.rs   | 10 +++++---
 3 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/juniper/src/executor.rs b/juniper/src/executor.rs
index f3d1852c..5e84c4b7 100644
--- a/juniper/src/executor.rs
+++ b/juniper/src/executor.rs
@@ -14,7 +14,7 @@ use parser::SourcePosition;
 
 use schema::meta::{Argument, EnumMeta, EnumValue, Field, InputObjectMeta, InterfaceMeta, ListMeta,
                    MetaType, NullableMeta, ObjectMeta, PlaceholderMeta, ScalarMeta, UnionMeta};
-use schema::model::{RootNode, SchemaType};
+use schema::model::{RootNode, SchemaType, TypeType};
 
 use types::base::GraphQLType;
 use types::name::Name;
@@ -46,6 +46,7 @@ where
     fragments: &'a HashMap<&'a str, &'a Fragment<'a>>,
     variables: &'a Variables,
     current_selection_set: Option<&'a [Selection<'a>]>,
+    current_type: TypeType<'a>,
     schema: &'a SchemaType<'a>,
     context: &'a CtxT,
     errors: &'a RwLock<Vec<ExecutionError>>,
@@ -305,6 +306,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
             fragments: self.fragments,
             variables: self.variables,
             current_selection_set: self.current_selection_set,
+            current_type: self.current_type.clone(),
             schema: self.schema,
             context: ctx,
             errors: self.errors,
@@ -313,9 +315,10 @@ impl<'a, CtxT> Executor<'a, CtxT> {
     }
 
     #[doc(hidden)]
-    pub fn sub_executor(
+    pub fn field_sub_executor(
         &self,
-        field_name: Option<&'a str>,
+        field_alias: &'a str,
+        field_name: &'a str,
         location: SourcePosition,
         selection_set: Option<&'a [Selection]>,
     ) -> Executor<CtxT> {
@@ -323,13 +326,36 @@ impl<'a, CtxT> Executor<'a, CtxT> {
             fragments: self.fragments,
             variables: self.variables,
             current_selection_set: selection_set,
+            current_type: self.schema.make_type(
+                &self.current_type
+                    .innermost_concrete()
+                    .field_by_name(field_name).expect("Field not found on inner type")
+                    .field_type),
             schema: self.schema,
             context: self.context,
             errors: self.errors,
-            field_path: match field_name {
-                Some(name) => FieldPath::Field(name, location, &self.field_path),
-                None => self.field_path.clone(),
+            field_path: FieldPath::Field(field_alias, location, &self.field_path),
+        }
+    }
+
+    #[doc(hidden)]
+    pub fn type_sub_executor(
+        &self,
+        type_name: Option<&'a str>,
+        selection_set: Option<&'a [Selection]>,
+    ) -> Executor<CtxT> {
+        Executor {
+            fragments: self.fragments,
+            variables: self.variables,
+            current_selection_set: selection_set,
+            current_type: match type_name {
+                Some(type_name) => self.schema.type_by_name(type_name).expect("Type not found"),
+                None => self.current_type.clone(),
             },
+            schema: self.schema,
+            context: self.context,
+            errors: self.errors,
+            field_path: self.field_path.clone(),
         }
     }
 
@@ -346,6 +372,11 @@ impl<'a, CtxT> Executor<'a, CtxT> {
         self.schema
     }
 
+    #[doc(hidden)]
+    pub fn current_type(&self) -> &TypeType<'a> {
+        &self.current_type
+    }
+
     #[doc(hidden)]
     pub fn variables(&self) -> &'a Variables {
         self.variables
@@ -492,6 +523,11 @@ where
             final_vars = &all_vars;
         }
 
+        let root_type = match op.item.operation_type {
+            OperationType::Query => root_node.schema.query_type(),
+            OperationType::Mutation => root_node.schema.mutation_type().expect("No mutation type found")
+        };
+
         let executor = Executor {
             fragments: &fragments
                 .iter()
@@ -499,6 +535,7 @@ where
                 .collect(),
             variables: final_vars,
             current_selection_set: Some(&op.item.selection_set[..]),
+            current_type: root_type,
             schema: &root_node.schema,
             context: context,
             errors: &errors,
diff --git a/juniper/src/schema/model.rs b/juniper/src/schema/model.rs
index 41fe51eb..6d2d2351 100644
--- a/juniper/src/schema/model.rs
+++ b/juniper/src/schema/model.rs
@@ -35,6 +35,7 @@ pub struct SchemaType<'a> {
 
 impl<'a> Context for SchemaType<'a> {}
 
+#[derive(Clone)]
 pub enum TypeType<'a> {
     Concrete(&'a MetaType<'a>),
     NonNull(Box<TypeType<'a>>),
diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs
index b88031e6..b1d29a70 100644
--- a/juniper/src/types/base.rs
+++ b/juniper/src/types/base.rs
@@ -367,8 +367,9 @@ fn resolve_selection_set_into<T, CtxT>(
 
                 let exec_vars = executor.variables();
 
-                let sub_exec = executor.sub_executor(
-                    Some(response_name),
+                let sub_exec = executor.field_sub_executor(
+                    response_name,
+                    &f.name.item,
                     start_pos.clone(),
                     f.selection_set.as_ref().map(|v| &v[..]),
                 );
@@ -434,8 +435,9 @@ fn resolve_selection_set_into<T, CtxT>(
                     continue;
                 }
 
-                let sub_exec = executor
-                    .sub_executor(None, start_pos.clone(), Some(&fragment.selection_set[..]));
+                let sub_exec = executor.type_sub_executor(
+                    fragment.type_condition.as_ref().map(|c| c.item),
+                    Some(&fragment.selection_set[..]));
 
                 if let Some(ref type_condition) = fragment.type_condition {
                     let sub_result = instance.resolve_into_type(