Pass the executor immutably through resolution
The executor is now using internal mutability through RwLock to make it thread-safe.
This commit is contained in:
parent
cd33093dee
commit
b61ca14f53
16 changed files with 55 additions and 51 deletions
|
@ -1,5 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use ::GraphQLError;
|
||||
use ast::{InputValue, ToInputValue, Document, Selection, Fragment, Definition, Type, FromInputValue, OperationType};
|
||||
|
@ -41,7 +42,7 @@ pub struct Executor<'a, CtxT> where CtxT: 'a {
|
|||
current_selection_set: Option<Vec<Selection>>,
|
||||
schema: &'a SchemaType,
|
||||
context: &'a CtxT,
|
||||
errors: &'a mut Vec<ExecutionError>,
|
||||
errors: &'a RwLock<Vec<ExecutionError>>,
|
||||
field_path: FieldPath<'a>,
|
||||
}
|
||||
|
||||
|
@ -81,7 +82,7 @@ impl<T> IntoFieldResult<T> for FieldResult<T> {
|
|||
|
||||
impl<'a, CtxT> Executor<'a, CtxT> {
|
||||
/// Resolve a single arbitrary value into an `ExecutionResult`
|
||||
pub fn resolve<T: GraphQLType<CtxT>>(&mut self, value: &T) -> ExecutionResult {
|
||||
pub fn resolve<T: GraphQLType<CtxT>>(&self, value: &T) -> ExecutionResult {
|
||||
Ok(value.resolve(
|
||||
match self.current_selection_set {
|
||||
Some(ref sel) => Some(sel.clone()),
|
||||
|
@ -93,7 +94,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
|
|||
/// Resolve a single arbitrary value into a return value
|
||||
///
|
||||
/// If the field fails to resolve, `null` will be returned.
|
||||
pub fn resolve_into_value<T: GraphQLType<CtxT>>(&mut self, value: &T) -> Value {
|
||||
pub fn resolve_into_value<T: GraphQLType<CtxT>>(&self, value: &T) -> Value {
|
||||
match self.resolve(value) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
|
@ -108,7 +109,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
|
|||
///
|
||||
/// This can be used to connect different types, e.g. from different Rust
|
||||
/// libraries, that require different context types.
|
||||
pub fn replaced_context<'b, NewCtxT>(&'b mut self, ctx: &'b NewCtxT) -> Executor<'b, NewCtxT> {
|
||||
pub fn replaced_context<'b, NewCtxT>(&'b self, ctx: &'b NewCtxT) -> Executor<'b, NewCtxT> {
|
||||
Executor {
|
||||
fragments: self.fragments,
|
||||
variables: self.variables,
|
||||
|
@ -122,7 +123,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn sub_executor(
|
||||
&mut self,
|
||||
&self,
|
||||
field_name: Option<String>,
|
||||
location: SourcePosition,
|
||||
selection_set: Option<Vec<Selection>>,
|
||||
|
@ -167,11 +168,13 @@ impl<'a, CtxT> Executor<'a, CtxT> {
|
|||
}
|
||||
|
||||
/// Add an error to the execution engine
|
||||
pub fn push_error(&mut self, error: String, location: SourcePosition) {
|
||||
pub fn push_error(&self, error: String, location: SourcePosition) {
|
||||
let mut path = Vec::new();
|
||||
self.field_path.construct_path(&mut path);
|
||||
|
||||
self.errors.push(ExecutionError {
|
||||
let mut errors = self.errors.write().unwrap();
|
||||
|
||||
errors.push(ExecutionError {
|
||||
location: location,
|
||||
path: path,
|
||||
message: error,
|
||||
|
@ -261,17 +264,17 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
|
|||
None => return Err(GraphQLError::UnknownOperationName),
|
||||
};
|
||||
|
||||
let mut errors = Vec::new();
|
||||
let errors = RwLock::new(Vec::new());
|
||||
let value;
|
||||
|
||||
{
|
||||
let mut executor = Executor {
|
||||
let executor = Executor {
|
||||
fragments: &fragments.into_iter().map(|f| (f.item.name.item.clone(), f.item)).collect(),
|
||||
variables: variables,
|
||||
current_selection_set: Some(op.item.selection_set),
|
||||
schema: &root_node.schema,
|
||||
context: context,
|
||||
errors: &mut errors,
|
||||
errors: &errors,
|
||||
field_path: FieldPath::Root(op.start),
|
||||
};
|
||||
|
||||
|
@ -281,6 +284,7 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
|
|||
};
|
||||
}
|
||||
|
||||
let mut errors = errors.into_inner().unwrap();
|
||||
errors.sort();
|
||||
|
||||
Ok((value, errors))
|
||||
|
|
|
@ -167,7 +167,7 @@ mod threads_context_correctly {
|
|||
struct Schema;
|
||||
|
||||
graphql_object!(Schema: String |&self| {
|
||||
field a(&mut executor) -> String { executor.context().clone() }
|
||||
field a(&executor) -> String { executor.context().clone() }
|
||||
});
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -67,7 +67,7 @@ graphql_object!(User: Database |&self| {
|
|||
//
|
||||
// In this example, the context is used to convert the friend_ids array
|
||||
// into actual User objects.
|
||||
field friends(&mut executor) -> Vec<&User> {
|
||||
field friends(&executor) -> Vec<&User> {
|
||||
self.friend_ids.iter()
|
||||
.filter_map(|id| executor.context().users.get(id))
|
||||
.collect()
|
||||
|
@ -79,7 +79,7 @@ graphql_object!(User: Database |&self| {
|
|||
graphql_object!(QueryRoot: Database |&self| {
|
||||
|
||||
// Arguments work just like they do on functions.
|
||||
field user(&mut executor, id: String) -> Option<&User> {
|
||||
field user(&executor, id: String) -> Option<&User> {
|
||||
executor.context().users.get(&id)
|
||||
}
|
||||
});
|
||||
|
@ -121,7 +121,7 @@ use juniper::iron_handlers::GraphQLHandler;
|
|||
# Ok(&self.name)
|
||||
# }
|
||||
#
|
||||
# field friends(&mut executor) -> FieldResult<Vec<&User>> {
|
||||
# field friends(&executor) -> FieldResult<Vec<&User>> {
|
||||
# Ok(self.friend_ids.iter()
|
||||
# .filter_map(|id| executor.context().users.get(id))
|
||||
# .collect())
|
||||
|
@ -129,7 +129,7 @@ use juniper::iron_handlers::GraphQLHandler;
|
|||
# });
|
||||
#
|
||||
# graphql_object!(QueryRoot: Database |&self| {
|
||||
# field user(&mut executor, id: String) -> FieldResult<Option<&User>> {
|
||||
# field user(&executor, id: String) -> FieldResult<Option<&User>> {
|
||||
# Ok(executor.context().users.get(&id))
|
||||
# }
|
||||
# });
|
||||
|
|
|
@ -15,9 +15,9 @@ macro_rules! __graphql__args {
|
|||
|
||||
(
|
||||
@assign_arg_vars,
|
||||
$args:ident, $executorvar:ident, &mut $exec:ident $($rest:tt)*
|
||||
$args:ident, $executorvar:ident, &$exec:ident $($rest:tt)*
|
||||
) => {
|
||||
let __graphql__args!(@as_pattern, $exec) = &mut $executorvar;
|
||||
let __graphql__args!(@as_pattern, $exec) = &$executorvar;
|
||||
__graphql__args!(@assign_arg_vars, $args, $executorvar, $($rest)*);
|
||||
};
|
||||
|
||||
|
@ -59,7 +59,7 @@ macro_rules! __graphql__args {
|
|||
|
||||
(
|
||||
@apply_args,
|
||||
$reg:expr, $base:expr, ( &mut executor $( $rest:tt )* )
|
||||
$reg:expr, $base:expr, ( &executor $( $rest:tt )* )
|
||||
) => {
|
||||
__graphql__args!(
|
||||
@apply_args,
|
||||
|
|
|
@ -81,7 +81,7 @@ macro_rules! graphql_enum {
|
|||
.into_meta()
|
||||
}
|
||||
|
||||
fn resolve(&self, _: Option<Vec<$crate::Selection>>, _: &mut $crate::Executor<CtxT>) -> $crate::Value {
|
||||
fn resolve(&self, _: Option<Vec<$crate::Selection>>, _: &$crate::Executor<CtxT>) -> $crate::Value {
|
||||
match self {
|
||||
$(
|
||||
&graphql_enum!(@as_pattern, $eval) =>
|
||||
|
|
|
@ -249,7 +249,7 @@ macro_rules! graphql_interface {
|
|||
|
||||
#[allow(unused_variables)]
|
||||
#[allow(unused_mut)]
|
||||
fn resolve_field(&$mainself, field: &str, args: &$crate::Arguments, mut executor: &mut $crate::Executor<$ctxt>) -> $crate::ExecutionResult {
|
||||
fn resolve_field(&$mainself, field: &str, args: &$crate::Arguments, mut executor: &$crate::Executor<$ctxt>) -> $crate::ExecutionResult {
|
||||
__graphql__build_field_matches!(
|
||||
($outname, $mainself, field, args, executor),
|
||||
(),
|
||||
|
@ -267,7 +267,7 @@ macro_rules! graphql_interface {
|
|||
&$mainself,
|
||||
type_name: &str,
|
||||
_: Option<Vec<$crate::Selection>>,
|
||||
executor: &mut $crate::Executor<$ctxt>,
|
||||
executor: &$crate::Executor<$ctxt>,
|
||||
)
|
||||
-> $crate::ExecutionResult
|
||||
{
|
||||
|
|
|
@ -194,7 +194,7 @@ string as documentation on the field.
|
|||
### Field arguments
|
||||
|
||||
```text
|
||||
&mut executor
|
||||
&executor
|
||||
arg_name: ArgType
|
||||
arg_name = default_value: ArgType
|
||||
arg_name: ArgType as "Argument description"
|
||||
|
@ -202,7 +202,7 @@ arg_name = default_value: ArgType as "Argument description"
|
|||
```
|
||||
|
||||
Field arguments can take many forms. If the field needs access to the executor
|
||||
or context, it can take an [Executor][1] instance by specifying `&mut executor`
|
||||
or context, it can take an [Executor][1] instance by specifying `&executor`
|
||||
as the first argument.
|
||||
|
||||
The other cases are similar to regular Rust arguments, with two additions:
|
||||
|
@ -382,7 +382,7 @@ macro_rules! graphql_object {
|
|||
&$mainself,
|
||||
field: &str,
|
||||
args: &$crate::Arguments,
|
||||
mut executor: &mut $crate::Executor<$ctxt>
|
||||
executor: &$crate::Executor<$ctxt>
|
||||
)
|
||||
-> $crate::ExecutionResult
|
||||
{
|
||||
|
|
|
@ -76,7 +76,7 @@ macro_rules! graphql_scalar {
|
|||
fn resolve(
|
||||
&$resolve_selfvar,
|
||||
_: Option<Vec<$crate::Selection>>,
|
||||
_: &mut $crate::Executor<CtxT>) -> $crate::Value {
|
||||
_: &$crate::Executor<CtxT>) -> $crate::Value {
|
||||
$resolve_body
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ Syntax to validate:
|
|||
|
||||
graphql_object!(Root: () |&self| {
|
||||
field simple() -> i64 { 0 }
|
||||
field exec_arg(&mut executor) -> i64 { 0 }
|
||||
field exec_arg_and_more(&mut executor, arg: i64) -> i64 { 0 }
|
||||
field exec_arg(&executor) -> i64 { 0 }
|
||||
field exec_arg_and_more(&executor, arg: i64) -> i64 { 0 }
|
||||
|
||||
field single_arg(arg: i64) -> i64 { 0 }
|
||||
field multi_args(
|
||||
|
|
|
@ -136,7 +136,7 @@ macro_rules! graphql_union {
|
|||
&$mainself,
|
||||
type_name: &str,
|
||||
_: Option<Vec<$crate::Selection>>,
|
||||
executor: &mut $crate::Executor<$ctxt>,
|
||||
executor: &$crate::Executor<$ctxt>,
|
||||
)
|
||||
-> $crate::ExecutionResult
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ impl<CtxT, QueryT, MutationT> GraphQLType<CtxT> for RootNode<CtxT, QueryT, Mutat
|
|||
QueryT::meta(registry)
|
||||
}
|
||||
|
||||
fn resolve_field(&self, field: &str, args: &Arguments, executor: &mut Executor<CtxT>) -> ExecutionResult {
|
||||
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult {
|
||||
match field {
|
||||
"__schema" => executor.replaced_context(&self.schema).resolve(&self.schema),
|
||||
"__type" => {
|
||||
|
@ -99,7 +99,7 @@ graphql_object!(<'a> TypeType<'a>: SchemaType as "__Type" |&self| {
|
|||
}
|
||||
}
|
||||
|
||||
field interfaces(&mut executor) -> Option<Vec<TypeType>> {
|
||||
field interfaces(&executor) -> Option<Vec<TypeType>> {
|
||||
match *self {
|
||||
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => {
|
||||
let schema = executor.context();
|
||||
|
@ -112,7 +112,7 @@ graphql_object!(<'a> TypeType<'a>: SchemaType as "__Type" |&self| {
|
|||
}
|
||||
}
|
||||
|
||||
field possible_types(&mut executor) -> Option<Vec<TypeType>> {
|
||||
field possible_types(&executor) -> Option<Vec<TypeType>> {
|
||||
let schema = executor.context();
|
||||
match *self {
|
||||
TypeType::Concrete(&MetaType::Union(UnionMeta { ref of_type_names, .. })) => {
|
||||
|
@ -162,7 +162,7 @@ graphql_object!(Field: SchemaType as "__Field" |&self| {
|
|||
self.arguments.as_ref().map_or_else(|| Vec::new(), |v| v.iter().collect())
|
||||
}
|
||||
|
||||
field type(&mut executor) -> TypeType {
|
||||
field type(&executor) -> TypeType {
|
||||
executor.context().make_type(&self.field_type)
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ graphql_object!(Argument: SchemaType as "__InputValue" |&self| {
|
|||
&self.description
|
||||
}
|
||||
|
||||
field type(&mut executor) -> TypeType {
|
||||
field type(&executor) -> TypeType {
|
||||
executor.context().make_type(&self.arg_type)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
|
|||
Some(self.name())
|
||||
}
|
||||
|
||||
field friends(&mut executor) -> Vec<&Character>
|
||||
field friends(&executor) -> Vec<&Character>
|
||||
as "The friends of the character" {
|
||||
executor.context().get_friends(self.as_character())
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ graphql_object!(<'a> &'a Human: Database as "Human" |&self| {
|
|||
Some(self.name())
|
||||
}
|
||||
|
||||
field friends(&mut executor) -> Vec<&Character>
|
||||
field friends(&executor) -> Vec<&Character>
|
||||
as "The friends of the human" {
|
||||
executor.context().get_friends(self.as_character())
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ graphql_object!(<'a> &'a Droid: Database as "Droid" |&self| {
|
|||
Some(self.name())
|
||||
}
|
||||
|
||||
field friends(&mut executor) -> Vec<&Character>
|
||||
field friends(&executor) -> Vec<&Character>
|
||||
as "The friends of the droid" {
|
||||
executor.context().get_friends(self.as_character())
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ impl GraphQLType<Database> for User {
|
|||
&self,
|
||||
field_name: &str,
|
||||
args: &Arguments,
|
||||
executor: &mut Executor<Database>
|
||||
executor: &Executor<Database>
|
||||
)
|
||||
-> ExecutionResult
|
||||
{
|
||||
|
@ -221,7 +221,7 @@ pub trait GraphQLType<CtxT>: Sized {
|
|||
///
|
||||
/// The default implementation panics.
|
||||
#[allow(unused_variables)]
|
||||
fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &mut Executor<CtxT>)
|
||||
fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &Executor<CtxT>)
|
||||
-> ExecutionResult
|
||||
{
|
||||
panic!("resolve_field must be implemented by object types");
|
||||
|
@ -234,7 +234,7 @@ pub trait GraphQLType<CtxT>: Sized {
|
|||
///
|
||||
/// The default implementation panics.
|
||||
#[allow(unused_variables)]
|
||||
fn resolve_into_type(&self, type_name: &str, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> ExecutionResult {
|
||||
fn resolve_into_type(&self, type_name: &str, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> ExecutionResult {
|
||||
if Self::name().unwrap() == type_name {
|
||||
Ok(self.resolve(selection_set, executor))
|
||||
} else {
|
||||
|
@ -260,7 +260,7 @@ pub trait GraphQLType<CtxT>: Sized {
|
|||
/// The default implementation uses `resolve_field` to resolve all fields,
|
||||
/// including those through fragment expansion, for object types. For
|
||||
/// non-object types, this method panics.
|
||||
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
|
||||
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
|
||||
if let Some(selection_set) = selection_set {
|
||||
let mut result = HashMap::new();
|
||||
resolve_selection_set_into(self, selection_set, executor, &mut result);
|
||||
|
@ -275,7 +275,7 @@ pub trait GraphQLType<CtxT>: Sized {
|
|||
fn resolve_selection_set_into<T, CtxT>(
|
||||
instance: &T,
|
||||
selection_set: Vec<Selection>,
|
||||
executor: &mut Executor<CtxT>,
|
||||
executor: &Executor<CtxT>,
|
||||
result: &mut HashMap<String, Value>)
|
||||
where T: GraphQLType<CtxT>
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@ impl<T, CtxT> GraphQLType<CtxT> for Option<T> where T: GraphQLType<CtxT> {
|
|||
registry.build_nullable_type::<T>().into_meta()
|
||||
}
|
||||
|
||||
fn resolve(&self, _: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
|
||||
fn resolve(&self, _: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
|
||||
match *self {
|
||||
Some(ref obj) => executor.resolve_into_value(obj),
|
||||
None => Value::null(),
|
||||
|
@ -59,7 +59,7 @@ impl<T, CtxT> GraphQLType<CtxT> for Vec<T> where T: GraphQLType<CtxT> {
|
|||
registry.build_list_type::<T>().into_meta()
|
||||
}
|
||||
|
||||
fn resolve(&self, _: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
|
||||
fn resolve(&self, _: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
|
||||
Value::list(
|
||||
self.iter().map(|e| executor.resolve_into_value(e)).collect()
|
||||
)
|
||||
|
@ -111,7 +111,7 @@ impl<'a, T, CtxT> GraphQLType<CtxT> for &'a [T] where T: GraphQLType<CtxT> {
|
|||
registry.build_list_type::<T>().into_meta()
|
||||
}
|
||||
|
||||
fn resolve(&self, _: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
|
||||
fn resolve(&self, _: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
|
||||
Value::list(
|
||||
self.iter().map(|e| executor.resolve_into_value(e)).collect()
|
||||
)
|
||||
|
|
|
@ -14,16 +14,16 @@ impl<T, CtxT> GraphQLType<CtxT> for Box<T> where T: GraphQLType<CtxT> {
|
|||
T::meta(registry)
|
||||
}
|
||||
|
||||
fn resolve_into_type(&self, name: &str, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> ExecutionResult {
|
||||
fn resolve_into_type(&self, name: &str, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> ExecutionResult {
|
||||
(**self).resolve_into_type(name, selection_set, executor)
|
||||
}
|
||||
|
||||
fn resolve_field(&self, field: &str, args: &Arguments, executor: &mut Executor<CtxT>) -> ExecutionResult
|
||||
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult
|
||||
{
|
||||
(**self).resolve_field(field, args, executor)
|
||||
}
|
||||
|
||||
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
|
||||
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
|
||||
(**self).resolve(selection_set, executor)
|
||||
}
|
||||
}
|
||||
|
@ -58,16 +58,16 @@ impl<'a, T, CtxT> GraphQLType<CtxT> for &'a T where T: GraphQLType<CtxT> {
|
|||
T::meta(registry)
|
||||
}
|
||||
|
||||
fn resolve_into_type(&self, name: &str, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> ExecutionResult {
|
||||
fn resolve_into_type(&self, name: &str, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> ExecutionResult {
|
||||
(**self).resolve_into_type(name, selection_set, executor)
|
||||
}
|
||||
|
||||
fn resolve_field(&self, field: &str, args: &Arguments, executor: &mut Executor<CtxT>) -> ExecutionResult
|
||||
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult
|
||||
{
|
||||
(**self).resolve_field(field, args, executor)
|
||||
}
|
||||
|
||||
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
|
||||
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
|
||||
(**self).resolve(selection_set, executor)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ impl<'a, CtxT> GraphQLType<CtxT> for &'a str {
|
|||
registry.build_scalar_type::<String>().into_meta()
|
||||
}
|
||||
|
||||
fn resolve(&self, _: Option<Vec<Selection>>, _: &mut Executor<CtxT>) -> Value {
|
||||
fn resolve(&self, _: Option<Vec<Selection>>, _: &Executor<CtxT>) -> Value {
|
||||
Value::string(self)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue