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.
This commit is contained in:
Magnus Hallin 2017-11-20 08:53:13 +01:00
parent 8cf2707faa
commit d0e9202f41
3 changed files with 50 additions and 10 deletions

View file

@ -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,

View file

@ -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>>),

View file

@ -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(