From 6b8f4c956277851902ac3c72ca0309284bb3afb7 Mon Sep 17 00:00:00 2001 From: Magnus Hallin <mhallin@fastmail.com> Date: Thu, 22 Dec 2016 16:25:39 +0100 Subject: [PATCH] Move context type parameter to associated type of GraphQLType --- examples/server.rs | 8 +- src/executor.rs | 84 +++++++++----- src/executor_tests/directives.rs | 3 +- src/executor_tests/enums.rs | 9 +- src/executor_tests/executor.rs | 23 ++-- src/executor_tests/interfaces_unions.rs | 6 +- src/executor_tests/introspection.rs | 11 +- src/executor_tests/variables.rs | 29 ++--- src/integrations/iron_handlers.rs | 17 +-- src/lib.rs | 23 ++-- src/macros/enums.rs | 8 +- src/macros/field.rs | 3 +- src/macros/input_object.rs | 6 +- src/macros/interface.rs | 16 +-- src/macros/object.rs | 8 +- src/macros/scalar.rs | 8 +- src/macros/tests/args.rs | 3 +- src/macros/tests/enums.rs | 3 +- src/macros/tests/field.rs | 3 +- src/macros/tests/input_object.rs | 3 +- src/macros/tests/interface.rs | 3 +- src/macros/tests/object.rs | 3 +- src/macros/tests/scalar.rs | 3 +- src/macros/tests/union.rs | 3 +- src/macros/union.rs | 14 ++- src/schema/model.rs | 105 +++++++---------- src/schema/schema.rs | 10 +- src/tests/introspection_tests.rs | 13 ++- src/tests/query_tests.rs | 25 +++-- src/tests/schema.rs | 3 + src/types/base.rs | 42 ++++--- src/types/containers.rs | 18 ++- src/types/pointers.rs | 12 +- src/types/scalars.rs | 52 ++++++++- .../rules/overlapping_fields_can_be_merged.rs | 66 +++++++---- src/validation/test_harness.rs | 106 ++++++++++++------ 36 files changed, 469 insertions(+), 283 deletions(-) diff --git a/examples/server.rs b/examples/server.rs index ec8d6c4a..e4ef3cbe 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -9,7 +9,7 @@ use std::env; use mount::Mount; use logger::Logger; use iron::prelude::*; -use juniper::FieldResult; +use juniper::EmptyMutation; use juniper::iron_handlers::{GraphQLHandler, GraphiQLHandler}; use juniper::tests::model::Database; @@ -20,7 +20,11 @@ fn context_factory(_: &mut Request) -> Database { fn main() { let mut mount = Mount::new(); - let graphql_endpoint = GraphQLHandler::new(context_factory, Database::new(), ()); + let graphql_endpoint = GraphQLHandler::new( + context_factory, + Database::new(), + EmptyMutation::<Database>::new(), + ); let graphiql_endpoint = GraphiQLHandler::new("/graphql"); mount.mount("/", graphiql_endpoint); diff --git a/src/executor.rs b/src/executor.rs index ae277917..4dceee46 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::marker::PhantomData; use std::sync::RwLock; use ::GraphQLError; @@ -20,10 +19,9 @@ use types::base::GraphQLType; /// The registry gathers metadata for all types in a schema. It provides /// convenience methods to convert types implementing the `GraphQLType` trait /// into `Type` instances and automatically registers them. -pub struct Registry<CtxT> { +pub struct Registry { /// Currently registered types pub types: HashMap<String, MetaType>, - phantom: PhantomData<CtxT>, } #[derive(Clone)] @@ -80,9 +78,46 @@ impl<T> IntoFieldResult<T> for FieldResult<T> { } } +/// Conversion trait for context types +/// +/// This is currently only used for converting arbitrary contexts into +/// the empty tuple, but will in the future be used to support general +/// context conversion for larger schemas. +pub trait FromContext<T> { + /// Perform the conversion + fn from_context(value: &T) -> &Self; +} + +/// Marker trait for types that can act as context objects for GraphQL types. +pub trait Context { } + +static NULL_CONTEXT: () = (); + +impl<T> FromContext<T> for () { + fn from_context(_: &T) -> &Self { + &NULL_CONTEXT + } +} + +impl<T> FromContext<T> for T where T: Context { + fn from_context(value: &T) -> &Self { + value + } +} + impl<'a, CtxT> Executor<'a, CtxT> { + /// Resolve a single arbitrary value, mapping the context to a new type + pub fn resolve_with_ctx<NewCtxT, T: GraphQLType<Context=NewCtxT>>( + &self, value: &T + ) -> ExecutionResult + where NewCtxT: FromContext<CtxT>, + { + self.replaced_context(<NewCtxT as FromContext<CtxT>>::from_context(&self.context)) + .resolve(value) + } + /// Resolve a single arbitrary value into an `ExecutionResult` - pub fn resolve<T: GraphQLType<CtxT>>(&self, value: &T) -> ExecutionResult { + pub fn resolve<T: GraphQLType<Context=CtxT>>(&self, value: &T) -> ExecutionResult { Ok(value.resolve( match self.current_selection_set { Some(ref sel) => Some(sel.clone()), @@ -94,7 +129,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>>(&self, value: &T) -> Value { + pub fn resolve_into_value<T: GraphQLType<Context=CtxT>>(&self, value: &T) -> Value { match self.resolve(value) { Ok(v) => v, Err(e) => { @@ -230,13 +265,13 @@ impl ExecutionError { pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>( document: Document, operation_name: Option<&str>, - root_node: &RootNode<CtxT, QueryT, MutationT>, + root_node: &RootNode<QueryT, MutationT>, variables: &HashMap<String, InputValue>, context: &CtxT ) -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>> - where QueryT: GraphQLType<CtxT>, - MutationT: GraphQLType<CtxT> + where QueryT: GraphQLType<Context=CtxT>, + MutationT: GraphQLType<Context=CtxT> { let mut fragments = vec![]; let mut operation = None; @@ -290,12 +325,11 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>( Ok((value, errors)) } -impl<CtxT> Registry<CtxT> { +impl Registry { /// Construct a new registry - pub fn new(types: HashMap<String, MetaType>) -> Registry<CtxT> { + pub fn new(types: HashMap<String, MetaType>) -> Registry { Registry { types: types, - phantom: PhantomData, } } @@ -303,7 +337,7 @@ impl<CtxT> Registry<CtxT> { /// /// If the registry hasn't seen a type with this name before, it will /// construct its metadata and store it. - pub fn get_type<T>(&mut self) -> Type where T: GraphQLType<CtxT> { + pub fn get_type<T>(&mut self) -> Type where T: GraphQLType { if let Some(name) = T::name() { if !self.types.contains_key(name) { self.insert_placeholder(name, Type::NonNullNamed(name.to_owned())); @@ -318,7 +352,7 @@ impl<CtxT> Registry<CtxT> { } /// Create a field with the provided name - pub fn field<T>(&mut self, name: &str) -> Field where T: GraphQLType<CtxT> { + pub fn field<T>(&mut self, name: &str) -> Field where T: GraphQLType { Field { name: name.to_owned(), description: None, @@ -329,7 +363,7 @@ impl<CtxT> Registry<CtxT> { } #[doc(hidden)] - pub fn field_convert<T: IntoFieldResult<I>, I>(&mut self, name: &str) -> Field where I: GraphQLType<CtxT> { + pub fn field_convert<T: IntoFieldResult<I>, I>(&mut self, name: &str) -> Field where I: GraphQLType { Field { name: name.to_owned(), description: None, @@ -340,7 +374,7 @@ impl<CtxT> Registry<CtxT> { } #[doc(hidden)] - pub fn field_inside_result<T>(&mut self, name: &str, _: FieldResult<T>) -> Field where T: GraphQLType<CtxT> { + pub fn field_inside_result<T>(&mut self, name: &str, _: FieldResult<T>) -> Field where T: GraphQLType { Field { name: name.to_owned(), description: None, @@ -351,7 +385,7 @@ impl<CtxT> Registry<CtxT> { } /// Create an argument with the provided name - pub fn arg<T>(&mut self, name: &str) -> Argument where T: GraphQLType<CtxT> + FromInputValue { + pub fn arg<T>(&mut self, name: &str) -> Argument where T: GraphQLType + FromInputValue { Argument::new(name, self.get_type::<T>()) } @@ -365,7 +399,7 @@ impl<CtxT> Registry<CtxT> { value: &T, ) -> Argument - where T: GraphQLType<CtxT> + ToInputValue + FromInputValue + where T: GraphQLType + ToInputValue + FromInputValue { Argument::new(name, self.get_type::<Option<T>>()) .default_value(value.to()) @@ -384,20 +418,20 @@ impl<CtxT> Registry<CtxT> { /// This expects the type to implement `FromInputValue`. pub fn build_scalar_type<T>(&mut self) -> ScalarMeta - where T: FromInputValue + GraphQLType<CtxT> + where T: FromInputValue + GraphQLType { let name = T::name().expect("Scalar types must be named. Implement name()"); ScalarMeta::new::<T>(name) } /// Create a list meta type - pub fn build_list_type<T: GraphQLType<CtxT>>(&mut self) -> ListMeta { + pub fn build_list_type<T: GraphQLType>(&mut self) -> ListMeta { let of_type = self.get_type::<T>(); ListMeta::new(of_type) } /// Create a nullable meta type - pub fn build_nullable_type<T: GraphQLType<CtxT>>(&mut self) -> NullableMeta { + pub fn build_nullable_type<T: GraphQLType>(&mut self) -> NullableMeta { let of_type = self.get_type::<T>(); NullableMeta::new(of_type) } @@ -408,7 +442,7 @@ impl<CtxT> Registry<CtxT> { /// function that needs to be called with the list of fields on the object. pub fn build_object_type<T>(&mut self) -> Box<Fn(&[Field]) -> ObjectMeta> - where T: GraphQLType<CtxT> + where T: GraphQLType { let name = T::name().expect("Object types must be named. Implement name()"); let typename_field = self.field::<String>("__typename"); @@ -423,7 +457,7 @@ impl<CtxT> Registry<CtxT> { /// Create an enum meta type pub fn build_enum_type<T>(&mut self) -> Box<Fn(&[EnumValue]) -> EnumMeta> - where T: FromInputValue + GraphQLType<CtxT> + where T: FromInputValue + GraphQLType { let name = T::name().expect("Enum types must be named. Implement name()"); @@ -433,7 +467,7 @@ impl<CtxT> Registry<CtxT> { /// Create an interface meta type builder pub fn build_interface_type<T>(&mut self) -> Box<Fn(&[Field]) -> InterfaceMeta> - where T: GraphQLType<CtxT> + where T: GraphQLType { let name = T::name().expect("Interface types must be named. Implement name()"); let typename_field = self.field::<String>("__typename"); @@ -448,7 +482,7 @@ impl<CtxT> Registry<CtxT> { /// Create a union meta type builder pub fn build_union_type<T>(&mut self) -> Box<Fn(&[Type]) -> UnionMeta> - where T: GraphQLType<CtxT> + where T: GraphQLType { let name = T::name().expect("Union types must be named. Implement name()"); @@ -458,7 +492,7 @@ impl<CtxT> Registry<CtxT> { /// Create an input object meta type builder pub fn build_input_object_type<T>(&mut self) -> Box<Fn(&[Argument]) -> InputObjectMeta> - where T: FromInputValue + GraphQLType<CtxT> + where T: FromInputValue + GraphQLType { let name = T::name().expect("Input object types must be named. Implement name()"); diff --git a/src/executor_tests/directives.rs b/src/executor_tests/directives.rs index e3acaf08..b681dd01 100644 --- a/src/executor_tests/directives.rs +++ b/src/executor_tests/directives.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use value::Value; use ast::InputValue; use schema::model::RootNode; +use types::scalars::EmptyMutation; struct TestType; @@ -19,7 +20,7 @@ graphql_object!(TestType: () |&self| { fn run_variable_query<F>(query: &str, vars: HashMap<String, InputValue>, f: F) where F: Fn(&HashMap<String, Value>) -> () { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let (result, errs) = ::execute(query, None, &schema, &vars, &()) .expect("Execution failed"); diff --git a/src/executor_tests/enums.rs b/src/executor_tests/enums.rs index effca654..bfec7ba2 100644 --- a/src/executor_tests/enums.rs +++ b/src/executor_tests/enums.rs @@ -6,6 +6,7 @@ use schema::model::RootNode; use ::GraphQLError::ValidationError; use validation::RuleError; use parser::SourcePosition; +use types::scalars::EmptyMutation; #[derive(Debug)] enum Color { Red, Green, Blue } @@ -30,7 +31,7 @@ graphql_object!(TestType: () |&self| { fn run_variable_query<F>(query: &str, vars: HashMap<String, InputValue>, f: F) where F: Fn(&HashMap<String, Value>) -> () { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let (result, errs) = ::execute(query, None, &schema, &vars, &()) .expect("Execution failed"); @@ -74,7 +75,7 @@ fn serializes_as_output() { #[test] fn does_not_accept_string_literals() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"{ toString(color: "RED") }"#; let vars = vec![ @@ -107,7 +108,7 @@ fn accepts_strings_in_variables() { #[test] fn does_not_accept_incorrect_enum_name_in_variables() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($color: Color!) { toString(color: $color) }"#; let vars = vec![ @@ -127,7 +128,7 @@ fn does_not_accept_incorrect_enum_name_in_variables() { #[test] fn does_not_accept_incorrect_type_in_variables() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($color: Color!) { toString(color: $color) }"#; let vars = vec![ diff --git a/src/executor_tests/executor.rs b/src/executor_tests/executor.rs index bf725156..b389dc92 100644 --- a/src/executor_tests/executor.rs +++ b/src/executor_tests/executor.rs @@ -2,6 +2,7 @@ mod field_execution { use value::Value; use ast::InputValue; use schema::model::RootNode; + use types::scalars::EmptyMutation; struct DataType; struct DeepDataType; @@ -33,7 +34,7 @@ mod field_execution { #[test] fn test() { - let schema = RootNode::new(DataType, ()); + let schema = RootNode::new(DataType, EmptyMutation::<()>::new()); let doc = r" query Example($size: Int) { a, @@ -109,6 +110,7 @@ mod field_execution { mod merge_parallel_fragments { use value::Value; use schema::model::RootNode; + use types::scalars::EmptyMutation; struct Type; @@ -121,7 +123,7 @@ mod merge_parallel_fragments { #[test] fn test() { - let schema = RootNode::new(Type, ()); + let schema = RootNode::new(Type, EmptyMutation::<()>::new()); let doc = r" { a, ...FragOne, ...FragTwo } fragment FragOne on Type { @@ -162,6 +164,7 @@ mod merge_parallel_fragments { mod threads_context_correctly { use value::Value; + use types::scalars::EmptyMutation; use schema::model::RootNode; struct Schema; @@ -172,7 +175,7 @@ mod threads_context_correctly { #[test] fn test() { - let schema = RootNode::new(Schema, ()); + let schema = RootNode::new(Schema, EmptyMutation::<String>::new()); let doc = r"{ a }"; let vars = vec![].into_iter().collect(); @@ -197,6 +200,7 @@ mod nulls_out_errors { use schema::model::RootNode; use executor::{ExecutionError, FieldResult}; use parser::SourcePosition; + use types::scalars::EmptyMutation; struct Schema; @@ -207,7 +211,7 @@ mod nulls_out_errors { #[test] fn test() { - let schema = RootNode::new(Schema, ()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ sync, syncError }"; let vars = vec![].into_iter().collect(); @@ -239,6 +243,7 @@ mod nulls_out_errors { mod named_operations { use value::Value; use schema::model::RootNode; + use types::scalars::EmptyMutation; use ::GraphQLError; struct Schema; @@ -249,7 +254,7 @@ mod named_operations { #[test] fn uses_inline_operation_if_no_name_provided() { - let schema = RootNode::new(Schema, ()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ a }"; let vars = vec![].into_iter().collect(); @@ -268,7 +273,7 @@ mod named_operations { #[test] fn uses_only_named_operation() { - let schema = RootNode::new(Schema, ()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { a }"; let vars = vec![].into_iter().collect(); @@ -287,7 +292,7 @@ mod named_operations { #[test] fn uses_named_operation_if_name_provided() { - let schema = RootNode::new(Schema, ()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); @@ -306,7 +311,7 @@ mod named_operations { #[test] fn error_if_multiple_operations_provided_but_no_name() { - let schema = RootNode::new(Schema, ()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); @@ -319,7 +324,7 @@ mod named_operations { #[test] fn error_if_unknown_operation_name_provided() { - let schema = RootNode::new(Schema, ()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); diff --git a/src/executor_tests/interfaces_unions.rs b/src/executor_tests/interfaces_unions.rs index b840787d..b3dd252c 100644 --- a/src/executor_tests/interfaces_unions.rs +++ b/src/executor_tests/interfaces_unions.rs @@ -1,6 +1,7 @@ mod interface { use value::Value; use schema::model::RootNode; + use types::scalars::EmptyMutation; trait Pet { fn name(&self) -> &str; @@ -71,7 +72,7 @@ mod interface { Box::new(Cat { name: "Garfield".to_owned(), meows: false }), ], }, - ()); + EmptyMutation::<()>::new()); let doc = r" { pets { @@ -118,6 +119,7 @@ mod interface { mod union { use value::Value; use schema::model::RootNode; + use types::scalars::EmptyMutation; trait Pet { fn as_dog(&self) -> Option<&Dog> { None } @@ -178,7 +180,7 @@ mod union { Box::new(Cat { name: "Garfield".to_owned(), meows: false }), ], }, - ()); + EmptyMutation::<()>::new()); let doc = r" { pets { diff --git a/src/executor_tests/introspection.rs b/src/executor_tests/introspection.rs index e9cdc672..03b725e3 100644 --- a/src/executor_tests/introspection.rs +++ b/src/executor_tests/introspection.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; enum Sample { One, @@ -67,7 +68,7 @@ fn test_execution() { second: sampleScalar(first: 10 second: 20) } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); @@ -104,7 +105,7 @@ fn enum_introspection() { } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); @@ -182,7 +183,7 @@ fn interface_introspection() { } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); @@ -283,7 +284,7 @@ fn object_introspection() { } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); @@ -395,7 +396,7 @@ fn scalar_introspection() { } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); diff --git a/src/executor_tests/variables.rs b/src/executor_tests/variables.rs index 76a930c7..0d2c78a7 100644 --- a/src/executor_tests/variables.rs +++ b/src/executor_tests/variables.rs @@ -6,6 +6,7 @@ use schema::model::RootNode; use ::GraphQLError::ValidationError; use validation::RuleError; use parser::SourcePosition; +use types::scalars::EmptyMutation; #[derive(Debug)] struct TestComplexScalar; @@ -88,7 +89,7 @@ graphql_object!(TestType: () |&self| { fn run_variable_query<F>(query: &str, vars: HashMap<String, InputValue>, f: F) where F: Fn(&HashMap<String, Value>) -> () { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let (result, errs) = ::execute(query, None, &schema, &vars, &()) .expect("Execution failed"); @@ -196,7 +197,7 @@ fn variable_runs_from_input_value_on_scalar() { #[test] fn variable_error_on_nested_non_null() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![ @@ -220,7 +221,7 @@ fn variable_error_on_nested_non_null() { #[test] fn variable_error_on_incorrect_type() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![ @@ -240,7 +241,7 @@ fn variable_error_on_incorrect_type() { #[test] fn variable_error_on_omit_non_null() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![ @@ -263,7 +264,7 @@ fn variable_error_on_omit_non_null() { #[test] fn variable_multiple_errors_with_nesting() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestNestedInputObject) { fieldWithNestedObjectInput(input: $input) }"#; let vars = vec![ @@ -291,7 +292,7 @@ fn variable_multiple_errors_with_nesting() { #[test] fn variable_error_on_additional_field() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![ @@ -377,7 +378,7 @@ fn allow_nullable_inputs_to_be_set_to_value_directly() { #[test] fn does_not_allow_non_nullable_input_to_be_omitted_in_variable() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#; let vars = vec![ @@ -396,7 +397,7 @@ fn does_not_allow_non_nullable_input_to_be_omitted_in_variable() { #[test] fn does_not_allow_non_nullable_input_to_be_set_to_null_in_variable() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#; let vars = vec![ @@ -489,7 +490,7 @@ fn allow_lists_to_contain_null() { #[test] fn does_not_allow_non_null_lists_to_be_null() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String]!) { nnList(input: $input) }"#; let vars = vec![ @@ -572,7 +573,7 @@ fn allow_lists_of_non_null_to_contain_values() { #[test] fn does_not_allow_lists_of_non_null_to_contain_null() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]) { listNn(input: $input) }"#; let vars = vec![ @@ -596,7 +597,7 @@ fn does_not_allow_lists_of_non_null_to_contain_null() { #[test] fn does_not_allow_non_null_lists_of_non_null_to_contain_null() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#; let vars = vec![ @@ -620,7 +621,7 @@ fn does_not_allow_non_null_lists_of_non_null_to_contain_null() { #[test] fn does_not_allow_non_null_lists_of_non_null_to_be_null() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#; let vars = vec![ @@ -656,7 +657,7 @@ fn allow_non_null_lists_of_non_null_to_contain_values() { #[test] fn does_not_allow_invalid_types_to_be_used_as_values() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestType!) { fieldWithObjectInput(input: $input) }"#; let vars = vec![ @@ -679,7 +680,7 @@ fn does_not_allow_invalid_types_to_be_used_as_values() { #[test] fn does_not_allow_unknown_types_to_be_used_as_values() { - let schema = RootNode::new(TestType, ()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: UnknownType!) { fieldWithObjectInput(input: $input) }"#; let vars = vec![ diff --git a/src/integrations/iron_handlers.rs b/src/integrations/iron_handlers.rs index 91c12bc5..339c75f9 100644 --- a/src/integrations/iron_handlers.rs +++ b/src/integrations/iron_handlers.rs @@ -25,11 +25,11 @@ use ::{InputValue, GraphQLType, RootNode, execute}; pub struct GraphQLHandler<CtxFactory, Query, Mutation, CtxT> where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static, CtxT: 'static, - Query: GraphQLType<CtxT> + Send + Sync + 'static, - Mutation: GraphQLType<CtxT> + Send + Sync + 'static, + Query: GraphQLType<Context=CtxT> + Send + Sync + 'static, + Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static, { context_factory: CtxFactory, - root_node: RootNode<CtxT, Query, Mutation>, + root_node: RootNode<Query, Mutation>, } /// Handler that renders GraphiQL - a graphical query editor interface @@ -41,8 +41,8 @@ impl<CtxFactory, Query, Mutation, CtxT> GraphQLHandler<CtxFactory, Query, Mutation, CtxT> where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static, CtxT: 'static, - Query: GraphQLType<CtxT> + Send + Sync + 'static, - Mutation: GraphQLType<CtxT> + Send + Sync + 'static, + Query: GraphQLType<Context=CtxT> + Send + Sync + 'static, + Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static, { /// Build a new GraphQL handler /// @@ -150,8 +150,8 @@ impl<CtxFactory, Query, Mutation, CtxT> for GraphQLHandler<CtxFactory, Query, Mutation, CtxT> where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static, CtxT: 'static, - Query: GraphQLType<CtxT> + Send + Sync + 'static, - Mutation: GraphQLType<CtxT> + Send + Sync + 'static, + Query: GraphQLType<Context=CtxT> + Send + Sync + 'static, + Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static, { fn handle(&self, req: &mut Request) -> IronResult<Response> { match req.method { @@ -248,6 +248,7 @@ mod tests { use iron::{Handler, Headers}; use ::tests::model::Database; + use types::scalars::EmptyMutation; use super::GraphQLHandler; @@ -259,7 +260,7 @@ mod tests { Box::new(GraphQLHandler::new( context_factory, Database::new(), - (), + EmptyMutation::<Database>::new(), )) } diff --git a/src/lib.rs b/src/lib.rs index 99fbcb6b..95df6f55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,14 +33,18 @@ existing object types as GraphQL objects: ```rust #[macro_use] extern crate juniper; # use std::collections::HashMap; -use juniper::FieldResult; +use juniper::{Context, FieldResult}; struct User { id: String, name: String, friend_ids: Vec<String> } struct QueryRoot; struct Database { users: HashMap<String, User> } +impl Context for Database {} + // GraphQL objects can access a "context object" during execution. Use this -// object to provide e.g. database access to the field accessors. +// object to provide e.g. database access to the field accessors. This object +// must implement the `Context` trait. If you don't need a context, use the +// empty tuple `()` to indicate this. // // In this example, we use the Database struct as our context. graphql_object!(User: Database |&self| { @@ -105,6 +109,7 @@ extern crate iron; use iron::prelude::*; use juniper::iron_handlers::GraphQLHandler; +use juniper::{Context, EmptyMutation}; # use juniper::FieldResult; # @@ -150,11 +155,14 @@ fn context_factory(_: &mut Request) -> Database { } } +impl Context for Database {} + fn main() { // GraphQLHandler takes a context factory function, the root object, // and the mutation object. If we don't have any mutations to expose, we // can use the empty tuple () to indicate absence. - let graphql_endpoint = GraphQLHandler::new(context_factory, QueryRoot, ()); + let graphql_endpoint = GraphQLHandler::new( + context_factory, QueryRoot, EmptyMutation::<Database>::new()); // Start serving the schema at the root on port 8080. Iron::new(graphql_endpoint).http("localhost:8080").unwrap(); @@ -212,10 +220,11 @@ pub use ast::{ToInputValue, FromInputValue, InputValue, Type, Selection}; pub use value::Value; pub use types::base::{Arguments, GraphQLType, TypeKind}; pub use executor::{ + Context, FromContext, Executor, Registry, ExecutionResult, ExecutionError, FieldResult, IntoFieldResult, }; pub use validation::RuleError; -pub use types::scalars::ID; +pub use types::scalars::{EmptyMutation, ID}; pub use schema::model::RootNode; pub use result_ext::ResultExt; @@ -238,13 +247,13 @@ pub enum GraphQLError<'a> { pub fn execute<'a, CtxT, QueryT, MutationT>( document_source: &'a str, operation_name: Option<&str>, - root_node: &RootNode<CtxT, QueryT, MutationT>, + root_node: &RootNode<QueryT, MutationT>, variables: &HashMap<String, InputValue>, context: &CtxT, ) -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>> - where QueryT: GraphQLType<CtxT>, - MutationT: GraphQLType<CtxT>, + where QueryT: GraphQLType<Context=CtxT>, + MutationT: GraphQLType<Context=CtxT>, { let document = try!(parse_document_source(document_source)); diff --git a/src/macros/enums.rs b/src/macros/enums.rs index 3e61c964..23424fdb 100644 --- a/src/macros/enums.rs +++ b/src/macros/enums.rs @@ -59,12 +59,14 @@ macro_rules! graphql_enum { ( $name:path, $outname:tt, $descr:tt ), [ $( ( $eval:tt, $ename:tt, $edescr:tt, $edepr:tt ) , )* ] ) => { - impl<CtxT> $crate::GraphQLType<CtxT> for $name { + impl $crate::GraphQLType for $name { + type Context = (); + fn name() -> Option<&'static str> { Some(graphql_enum!(@as_expr, $outname)) } - fn meta(registry: &mut $crate::Registry<CtxT>) -> $crate::meta::MetaType { + fn meta(registry: &mut $crate::Registry) -> $crate::meta::MetaType { graphql_enum!( @maybe_apply, $descr, description, registry.build_enum_type::<$name>()(&[ @@ -81,7 +83,7 @@ macro_rules! graphql_enum { .into_meta() } - fn resolve(&self, _: Option<Vec<$crate::Selection>>, _: &$crate::Executor<CtxT>) -> $crate::Value { + fn resolve(&self, _: Option<Vec<$crate::Selection>>, _: &$crate::Executor<Self::Context>) -> $crate::Value { match self { $( &graphql_enum!(@as_pattern, $eval) => diff --git a/src/macros/field.rs b/src/macros/field.rs index a03d6a12..64bff58d 100644 --- a/src/macros/field.rs +++ b/src/macros/field.rs @@ -77,7 +77,8 @@ macro_rules! __graphql__build_field_matches { $body })(); - return ($crate::IntoFieldResult::into(result)).and_then(|r| $executorvar.resolve(&r)) + return ($crate::IntoFieldResult::into(result)).and_then( + |r| $executorvar.resolve_with_ctx(&r)) } )* panic!("Field {} not found on type {}", $fieldvar, $outname); diff --git a/src/macros/input_object.rs b/src/macros/input_object.rs index 90b04077..7c76f2b2 100644 --- a/src/macros/input_object.rs +++ b/src/macros/input_object.rs @@ -148,12 +148,14 @@ macro_rules! graphql_input_object { } } - impl<CtxT> $crate::GraphQLType<CtxT> for $name { + impl $crate::GraphQLType for $name { + type Context = (); + fn name() -> Option<&'static str> { Some($outname) } - fn meta(registry: &mut $crate::Registry<CtxT>) -> $crate::meta::MetaType { + fn meta(registry: &mut $crate::Registry) -> $crate::meta::MetaType { graphql_input_object!( @maybe_apply, $descr, description, registry.build_input_object_type::<$name>()( diff --git a/src/macros/interface.rs b/src/macros/interface.rs index 01d27d42..41cb96a4 100644 --- a/src/macros/interface.rs +++ b/src/macros/interface.rs @@ -192,7 +192,7 @@ macro_rules! graphql_interface { $( if let Some(_) = $resolver as Option<$srctype> { - return (<$srctype as $crate::GraphQLType<$ctxttype>>::name()).unwrap().to_owned(); + return (<$srctype as $crate::GraphQLType>::name()).unwrap().to_owned(); } )* @@ -208,7 +208,7 @@ macro_rules! graphql_interface { let $ctxtvar = &$execarg.context(); $( - if $typenamearg == (<$srctype as $crate::GraphQLType<$ctxttype>>::name()).unwrap().to_owned() { + if $typenamearg == (<$srctype as $crate::GraphQLType>::name()).unwrap().to_owned() { return $execarg.resolve(&$resolver); } )* @@ -227,14 +227,16 @@ macro_rules! graphql_interface { $( $items:tt )* } ) => { - graphql_interface!(@as_item, impl<$($lifetime)*> $crate::GraphQLType<$ctxt> for $name { + graphql_interface!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name { + type Context = $ctxt; + fn name() -> Option<&'static str> { Some($outname) } #[allow(unused_assignments)] #[allow(unused_mut)] - fn meta(registry: &mut $crate::Registry<$ctxt>) -> $crate::meta::MetaType { + fn meta(registry: &mut $crate::Registry) -> $crate::meta::MetaType { let mut fields = Vec::new(); let mut description = None; graphql_interface!(@ gather_meta, (registry, fields, description), $($items)*); @@ -249,14 +251,14 @@ macro_rules! graphql_interface { #[allow(unused_variables)] #[allow(unused_mut)] - fn resolve_field(&$mainself, field: &str, args: &$crate::Arguments, mut executor: &$crate::Executor<$ctxt>) -> $crate::ExecutionResult { + fn resolve_field(&$mainself, field: &str, args: &$crate::Arguments, mut executor: &$crate::Executor<Self::Context>) -> $crate::ExecutionResult { __graphql__build_field_matches!( ($outname, $mainself, field, args, executor), (), $($items)*); } - fn concrete_type_name(&$mainself, context: &$ctxt) -> String { + fn concrete_type_name(&$mainself, context: &Self::Context) -> String { graphql_interface!( @ concrete_type_name, ($outname, context, $ctxt), @@ -267,7 +269,7 @@ macro_rules! graphql_interface { &$mainself, type_name: &str, _: Option<Vec<$crate::Selection>>, - executor: &$crate::Executor<$ctxt>, + executor: &$crate::Executor<Self::Context>, ) -> $crate::ExecutionResult { diff --git a/src/macros/object.rs b/src/macros/object.rs index 5330f0ed..fa440538 100644 --- a/src/macros/object.rs +++ b/src/macros/object.rs @@ -348,14 +348,16 @@ macro_rules! graphql_object { ( $($lifetime:tt)* ); $name:ty; $ctxt:ty; $outname:expr; $mainself:ident; $($items:tt)* ) => { - graphql_object!(@as_item, impl<$($lifetime)*> $crate::GraphQLType<$ctxt> for $name { + graphql_object!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name { + type Context = $ctxt; + fn name() -> Option<&'static str> { Some($outname) } #[allow(unused_assignments)] #[allow(unused_mut)] - fn meta(registry: &mut $crate::Registry<$ctxt>) -> $crate::meta::MetaType { + fn meta(registry: &mut $crate::Registry) -> $crate::meta::MetaType { let mut fields = Vec::new(); let mut description = None; let mut interfaces: Option<Vec<$crate::Type>> = None; @@ -382,7 +384,7 @@ macro_rules! graphql_object { &$mainself, field: &str, args: &$crate::Arguments, - executor: &$crate::Executor<$ctxt> + executor: &$crate::Executor<Self::Context> ) -> $crate::ExecutionResult { diff --git a/src/macros/scalar.rs b/src/macros/scalar.rs index ad0d3253..269745e4 100644 --- a/src/macros/scalar.rs +++ b/src/macros/scalar.rs @@ -61,12 +61,14 @@ macro_rules! graphql_scalar { ( $fiv_arg:ident, $fiv_result:ty, $fiv_body:block ) ) ) => { - impl<CtxT> $crate::GraphQLType<CtxT> for $name { + impl $crate::GraphQLType for $name { + type Context = (); + fn name() -> Option<&'static str> { Some($outname) } - fn meta(registry: &mut $crate::Registry<CtxT>) -> $crate::meta::MetaType { + fn meta(registry: &mut $crate::Registry) -> $crate::meta::MetaType { graphql_scalar!( @maybe_apply, $descr, description, registry.build_scalar_type::<Self>()) @@ -76,7 +78,7 @@ macro_rules! graphql_scalar { fn resolve( &$resolve_selfvar, _: Option<Vec<$crate::Selection>>, - _: &$crate::Executor<CtxT>) -> $crate::Value { + _: &$crate::Executor<Self::Context>) -> $crate::Value { $resolve_body } } diff --git a/src/macros/tests/args.rs b/src/macros/tests/args.rs index f677d43b..37d16eec 100644 --- a/src/macros/tests/args.rs +++ b/src/macros/tests/args.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; struct Root; @@ -87,7 +88,7 @@ fn run_args_info_query<F>(field_name: &str, f: F) } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); diff --git a/src/macros/tests/enums.rs b/src/macros/tests/enums.rs index 0576ecf9..db2bc686 100644 --- a/src/macros/tests/enums.rs +++ b/src/macros/tests/enums.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; enum DefaultName { Foo, Bar } @@ -67,7 +68,7 @@ graphql_object!(Root: () |&self| { }); fn run_type_info_query<F>(doc: &str, f: F) where F: Fn((&HashMap<String, Value>, &Vec<Value>)) -> () { - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); diff --git a/src/macros/tests/field.rs b/src/macros/tests/field.rs index 16d6e8cd..2ac68c08 100644 --- a/src/macros/tests/field.rs +++ b/src/macros/tests/field.rs @@ -4,6 +4,7 @@ use value::Value; use ast::InputValue; use schema::model::RootNode; use executor::FieldResult; +use types::scalars::EmptyMutation; struct Interface; struct Root; @@ -71,7 +72,7 @@ fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F) } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let vars = vec![ ("typeName".to_owned(), InputValue::string(type_name)), ].into_iter().collect(); diff --git a/src/macros/tests/input_object.rs b/src/macros/tests/input_object.rs index 28835e56..3999a201 100644 --- a/src/macros/tests/input_object.rs +++ b/src/macros/tests/input_object.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use ast::{InputValue, FromInputValue}; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; struct Root; @@ -62,7 +63,7 @@ graphql_object!(Root: () |&self| { }); fn run_type_info_query<F>(doc: &str, f: F) where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> () { - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); diff --git a/src/macros/tests/interface.rs b/src/macros/tests/interface.rs index 6afa25c8..b4c0e87d 100644 --- a/src/macros/tests/interface.rs +++ b/src/macros/tests/interface.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use ast::InputValue; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; /* @@ -142,7 +143,7 @@ fn run_type_info_query<F>(type_name: &str, f: F) } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let vars = vec![ ("typeName".to_owned(), InputValue::string(type_name)), ].into_iter().collect(); diff --git a/src/macros/tests/object.rs b/src/macros/tests/object.rs index 63d8815d..c00d5df9 100644 --- a/src/macros/tests/object.rs +++ b/src/macros/tests/object.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use ast::InputValue; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; /* @@ -130,7 +131,7 @@ fn run_type_info_query<F>(type_name: &str, f: F) } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let vars = vec![ ("typeName".to_owned(), InputValue::string(type_name)), ].into_iter().collect(); diff --git a/src/macros/tests/scalar.rs b/src/macros/tests/scalar.rs index 0cb832ca..c8744a22 100644 --- a/src/macros/tests/scalar.rs +++ b/src/macros/tests/scalar.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; struct DefaultName(i64); struct OtherOrder(i64); @@ -69,7 +70,7 @@ graphql_object!(Root: () |&self| { }); fn run_type_info_query<F>(doc: &str, f: F) where F: Fn(&HashMap<String, Value>) -> () { - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &HashMap::new(), &()) .expect("Execution failed"); diff --git a/src/macros/tests/union.rs b/src/macros/tests/union.rs index 12d2c616..6bfd560b 100644 --- a/src/macros/tests/union.rs +++ b/src/macros/tests/union.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use ast::InputValue; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; /* @@ -109,7 +110,7 @@ fn run_type_info_query<F>(type_name: &str, f: F) } } "#; - let schema = RootNode::new(Root {}, ()); + let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let vars = vec![ ("typeName".to_owned(), InputValue::string(type_name)), ].into_iter().collect(); diff --git a/src/macros/union.rs b/src/macros/union.rs index 5a4dd0c6..0a78f26b 100644 --- a/src/macros/union.rs +++ b/src/macros/union.rs @@ -62,7 +62,7 @@ macro_rules! graphql_union { $( if let Some(_) = $resolver as Option<$srctype> { - return (<$srctype as $crate::GraphQLType<$ctxttype>>::name()).unwrap().to_owned(); + return (<$srctype as $crate::GraphQLType>::name()).unwrap().to_owned(); } )* @@ -79,7 +79,7 @@ macro_rules! graphql_union { let $ctxtvar = &$execarg.context(); $( - if $typenamearg == (<$srctype as $crate::GraphQLType<$ctxttype>>::name()).unwrap().to_owned() { + if $typenamearg == (<$srctype as $crate::GraphQLType>::name()).unwrap().to_owned() { return $execarg.resolve(&$resolver); } )* @@ -105,14 +105,16 @@ macro_rules! graphql_union { $( $items:tt )* } ) => { - graphql_union!(@as_item, impl<$($lifetime)*> $crate::GraphQLType<$ctxt> for $name { + graphql_union!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name { + type Context = $ctxt; + fn name() -> Option<&'static str> { Some($outname) } #[allow(unused_assignments)] #[allow(unused_mut)] - fn meta(registry: &mut $crate::Registry<$ctxt>) -> $crate::meta::MetaType { + fn meta(registry: &mut $crate::Registry) -> $crate::meta::MetaType { let mut types; let mut description = None; graphql_union!(@ gather_meta, (registry, types, description), $($items)*); @@ -125,7 +127,7 @@ macro_rules! graphql_union { mt.into_meta() } - fn concrete_type_name(&$mainself, context: &$ctxt) -> String { + fn concrete_type_name(&$mainself, context: &Self::Context) -> String { graphql_union!( @ concrete_type_name, ($outname, context, $ctxt), @@ -136,7 +138,7 @@ macro_rules! graphql_union { &$mainself, type_name: &str, _: Option<Vec<$crate::Selection>>, - executor: &$crate::Executor<$ctxt>, + executor: &$crate::Executor<Self::Context>, ) -> $crate::ExecutionResult { diff --git a/src/schema/model.rs b/src/schema/model.rs index 8836b638..870ac477 100644 --- a/src/schema/model.rs +++ b/src/schema/model.rs @@ -1,37 +1,24 @@ use std::collections::HashMap; -use std::marker::PhantomData; use std::fmt; use types::base::{GraphQLType}; -use executor::Registry; +use executor::{Registry, Context}; use ast::Type; use schema::meta::{MetaType, ObjectMeta, PlaceholderMeta, UnionMeta, InterfaceMeta, Argument}; /// Root query node of a schema /// -/// This brings the mutatino and query types together, and provides the +/// This brings the mutation and query types together, and provides the /// predefined metadata fields. -pub struct RootNode<InnerT, QueryT, MutationT=()> { +pub struct RootNode<QueryT, MutationT> { #[doc(hidden)] pub query_type: QueryT, #[doc(hidden)] pub mutation_type: MutationT, #[doc(hidden)] pub schema: SchemaType, - phantom_wrapped: PhantomData<InnerT>, } -// RootNode implements Send + Sync if both the mutation type and query -// type implements them. SchemaType also needs to implement them. -// -// InnerT does _not_ need to implement Send + Sync because it does not -// actually appear in the struct. -unsafe impl<InnerT, QueryT, MutationT> Send for RootNode<InnerT, QueryT, MutationT> - where QueryT: Send, MutationT: Send, SchemaType: Send { } - -unsafe impl<InnerT, QueryT, MutationT> Sync for RootNode<InnerT, QueryT, MutationT> - where QueryT: Sync, MutationT: Sync, SchemaType: Sync { } - /// Metadata for a schema pub struct SchemaType { types: HashMap<String, MetaType>, @@ -40,6 +27,8 @@ pub struct SchemaType { directives: HashMap<String, DirectiveType>, } +impl Context for SchemaType {} + pub enum TypeType<'a> { Concrete(&'a MetaType), NonNull(Box<TypeType<'a>>), @@ -63,82 +52,72 @@ pub enum DirectiveLocation { InlineFragment, } -impl<InnerT, QueryT, MutationT> RootNode<InnerT, QueryT, MutationT> - where QueryT: GraphQLType<InnerT>, - MutationT: GraphQLType<InnerT>, +impl<QueryT, MutationT> RootNode<QueryT, MutationT> + where QueryT: GraphQLType, + MutationT: GraphQLType, { /// Construct a new root node from query and mutation nodes /// - /// If the schema should not support mutations, you can pass in `()` to - /// remove the mutation type from the schema. - pub fn new(query_obj: QueryT, mutation_obj: MutationT) -> RootNode<InnerT, QueryT, MutationT> { + /// If the schema should not support mutations, use the + /// `new` constructor instead. + pub fn new(query_obj: QueryT, mutation_obj: MutationT) -> RootNode<QueryT, MutationT> { RootNode { query_type: query_obj, mutation_type: mutation_obj, - schema: SchemaType::new::<InnerT, QueryT, MutationT>(), - phantom_wrapped: PhantomData, + schema: SchemaType::new::<QueryT, MutationT>(), } } } impl SchemaType { - pub fn new<CtxT, QueryT, MutationT>() -> SchemaType - where QueryT: GraphQLType<CtxT>, - MutationT: GraphQLType<CtxT>, + pub fn new<QueryT, MutationT>() -> SchemaType + where QueryT: GraphQLType, + MutationT: GraphQLType, { - let mut types = HashMap::new(); let mut directives = HashMap::new(); let query_type_name: String; let mutation_type_name: String; - { - let mut registry = Registry::<CtxT>::new(types); - query_type_name = registry.get_type::<QueryT>().innermost_name().to_owned(); - mutation_type_name = registry.get_type::<MutationT>().innermost_name().to_owned(); - types = registry.types; - } + let mut registry = Registry::new(HashMap::new()); + query_type_name = registry.get_type::<QueryT>().innermost_name().to_owned(); + mutation_type_name = registry.get_type::<MutationT>().innermost_name().to_owned(); - { - let mut registry = Registry::<SchemaType>::new(types); - registry.get_type::<SchemaType>(); - directives.insert( - "skip".to_owned(), - DirectiveType::new_skip(&mut registry)); - directives.insert( - "include".to_owned(), - DirectiveType::new_include(&mut registry)); + registry.get_type::<SchemaType>(); + directives.insert( + "skip".to_owned(), + DirectiveType::new_skip(&mut registry)); + directives.insert( + "include".to_owned(), + DirectiveType::new_include(&mut registry)); - let mut meta_fields = vec![ - registry.field::<SchemaType>("__schema"), - registry.field::<TypeType>("__type") - .argument(registry.arg::<String>("name")), - ]; + let mut meta_fields = vec![ + registry.field::<SchemaType>("__schema"), + registry.field::<TypeType>("__type") + .argument(registry.arg::<String>("name")), + ]; - if let Some(root_type) = registry.types.get_mut(&query_type_name) { - if let &mut MetaType::Object(ObjectMeta { ref mut fields, .. }) = root_type { - fields.append(&mut meta_fields); - } - else { - panic!("Root type is not an object"); - } + if let Some(root_type) = registry.types.get_mut(&query_type_name) { + if let &mut MetaType::Object(ObjectMeta { ref mut fields, .. }) = root_type { + fields.append(&mut meta_fields); } else { - panic!("Root type not found"); + panic!("Root type is not an object"); } - - types = registry.types; + } + else { + panic!("Root type not found"); } - for meta_type in types.values() { + for meta_type in registry.types.values() { if let MetaType::Placeholder(PlaceholderMeta { ref of_type }) = *meta_type { panic!("Type {:?} is still a placeholder type", of_type); } } SchemaType { - types: types, + types: registry.types, query_type_name: query_type_name, - mutation_type_name: if &mutation_type_name != "__Unit" { Some(mutation_type_name) } else { None }, + mutation_type_name: if &mutation_type_name != "__EmptyMutation" { Some(mutation_type_name) } else { None }, directives: directives, } } @@ -305,7 +284,7 @@ impl DirectiveType { } } - fn new_skip<CtxT>(registry: &mut Registry<CtxT>) -> DirectiveType { + fn new_skip(registry: &mut Registry) -> DirectiveType { Self::new( "skip", &[ @@ -318,7 +297,7 @@ impl DirectiveType { ]) } - fn new_include<CtxT>(registry: &mut Registry<CtxT>) -> DirectiveType { + fn new_include(registry: &mut Registry) -> DirectiveType { Self::new( "include", &[ diff --git a/src/schema/schema.rs b/src/schema/schema.rs index 9e3d580c..a7fca03e 100644 --- a/src/schema/schema.rs +++ b/src/schema/schema.rs @@ -7,15 +7,17 @@ use schema::meta::{MetaType, ObjectMeta, EnumMeta, InputObjectMeta, UnionMeta, I Field, Argument, EnumValue}; use schema::model::{RootNode, SchemaType, TypeType, DirectiveType, DirectiveLocation}; -impl<CtxT, QueryT, MutationT> GraphQLType<CtxT> for RootNode<CtxT, QueryT, MutationT> - where QueryT: GraphQLType<CtxT>, - MutationT: GraphQLType<CtxT> +impl<CtxT, QueryT, MutationT> GraphQLType for RootNode<QueryT, MutationT> + where QueryT: GraphQLType<Context=CtxT>, + MutationT: GraphQLType<Context=CtxT> { + type Context = CtxT; + fn name() -> Option<&'static str> { QueryT::name() } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { QueryT::meta(registry) } diff --git a/src/tests/introspection_tests.rs b/src/tests/introspection_tests.rs index 3ffa6326..97d7c587 100644 --- a/src/tests/introspection_tests.rs +++ b/src/tests/introspection_tests.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet}; use value::Value; use schema::model::RootNode; use tests::model::Database; +use types::scalars::EmptyMutation; #[test] fn test_query_type_name() { @@ -15,7 +16,7 @@ fn test_query_type_name() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -38,7 +39,7 @@ fn test_specific_type_name() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -61,7 +62,7 @@ fn test_specific_object_type_name_and_kind() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -85,7 +86,7 @@ fn test_specific_interface_type_name_and_kind() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -109,7 +110,7 @@ fn test_documentation() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -135,7 +136,7 @@ fn test_possible_types() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); let result = ::execute(doc, None, &schema, &HashMap::new(), &database); diff --git a/src/tests/query_tests.rs b/src/tests/query_tests.rs index 3f128049..c9325efd 100644 --- a/src/tests/query_tests.rs +++ b/src/tests/query_tests.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use ast::InputValue; use value::Value; use schema::model::RootNode; +use types::scalars::EmptyMutation; use tests::model::Database; #[test] @@ -14,7 +15,7 @@ fn test_hero_name() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -39,7 +40,7 @@ fn test_hero_name_and_friends() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -80,7 +81,7 @@ fn test_hero_name_and_friends_and_friends_of_friends() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -162,7 +163,7 @@ fn test_hero_name_and_friends_and_friends_of_friends() { fn test_query_name() { let doc = r#"{ human(id: "1000") { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -178,7 +179,7 @@ fn test_query_name() { fn test_query_alias_single() { let doc = r#"{ luke: human(id: "1000") { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -198,7 +199,7 @@ fn test_query_alias_multiple() { leia: human(id: "1003") { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -226,7 +227,7 @@ fn test_query_alias_multiple_with_fragment() { homePlanet }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -247,7 +248,7 @@ fn test_query_alias_multiple_with_fragment() { fn test_query_name_variable() { let doc = r#"query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); let vars = vec![ ("someId".to_owned(), InputValue::string("1000")), @@ -267,7 +268,7 @@ fn test_query_name_variable() { fn test_query_name_invalid_variable() { let doc = r#"query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); let vars = vec![ ("someId".to_owned(), InputValue::string("some invalid id")), @@ -285,7 +286,7 @@ fn test_query_name_invalid_variable() { fn test_query_friends_names() { let doc = r#"{ human(id: "1000") { friends { name } } }"#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -325,7 +326,7 @@ fn test_query_inline_fragments_droid() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), @@ -350,7 +351,7 @@ fn test_query_inline_fragments_human() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, ()); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); assert_eq!( ::execute(doc, None, &schema, &HashMap::new(), &database), diff --git a/src/tests/schema.rs b/src/tests/schema.rs index e1182d68..0e36b721 100644 --- a/src/tests/schema.rs +++ b/src/tests/schema.rs @@ -1,4 +1,7 @@ use tests::model::{Character, Human, Droid, Database, Episode}; +use executor::Context; + +impl Context for Database {} graphql_enum!(Episode { Episode::NewHope => "NEW_HOPE", diff --git a/src/types/base.rs b/src/types/base.rs index 3585b73e..d972a237 100644 --- a/src/types/base.rs +++ b/src/types/base.rs @@ -135,7 +135,7 @@ equivalent of the `User` object as shown in the example in the documentation root: ```rust -use juniper::{GraphQLType, Registry, FieldResult, +use juniper::{GraphQLType, Registry, FieldResult, Context, Arguments, Executor, ExecutionResult}; use juniper::meta::MetaType; # use std::collections::HashMap; @@ -143,15 +143,19 @@ use juniper::meta::MetaType; struct User { id: String, name: String, friend_ids: Vec<String> } struct Database { users: HashMap<String, User> } -impl GraphQLType<Database> for User { +impl Context for Database {} + +impl GraphQLType for User { + type Context = Database; + fn name() -> Option<&'static str> { Some("User") } - fn meta(registry: &mut Registry<Database>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { // First, we need to define all fields and their types on this type. // - // If need arguments, want to implement interfaces, or want to add + // If we need arguments, want to implement interfaces, or want to add // documentation strings, we can do it here. registry.build_object_type::<User>()(&[ registry.field::<&String>("id"), @@ -171,12 +175,15 @@ impl GraphQLType<Database> for User { { // Next, we need to match the queried field name. All arms of this // match statement return `ExecutionResult`, which makes it hard to - // statically verify that the type you pass on to `executor.resolve` + // statically verify that the type you pass on to `executor.resolve*` // actually matches the one that you defined in `meta()` above. let database = executor.context(); match field_name { - "id" => executor.resolve(&self.id), - "name" => executor.resolve(&self.name), + // Because scalars are defined with another `Context` associated + // type, you must use resolve_with_ctx here to make the executor + // perform automatic type conversion of its argument. + "id" => executor.resolve_with_ctx(&self.id), + "name" => executor.resolve_with_ctx(&self.name), // You pass a vector of User objects to `executor.resolve`, and it // will determine which fields of the sub-objects to actually @@ -201,7 +208,14 @@ impl GraphQLType<Database> for User { ``` */ -pub trait GraphQLType<CtxT>: Sized { +pub trait GraphQLType: Sized { + /// The expected context type for this GraphQL type + /// + /// The context is threaded through query execution to all affected nodes, + /// and can be used to hold common data, e.g. database connections or + /// request session information. + type Context; + /// The name of the GraphQL type to expose. /// /// This function will be called multiple times during schema construction. @@ -210,7 +224,7 @@ pub trait GraphQLType<CtxT>: Sized { fn name() -> Option<&'static str>; /// The meta type representing this GraphQL type. - fn meta(registry: &mut Registry<CtxT>) -> MetaType; + fn meta(registry: &mut Registry) -> MetaType; /// Resolve the value of a single field on this type. /// @@ -221,7 +235,7 @@ pub trait GraphQLType<CtxT>: Sized { /// /// The default implementation panics. #[allow(unused_variables)] - fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &Executor<CtxT>) + fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &Executor<Self::Context>) -> ExecutionResult { panic!("resolve_field must be implemented by object types"); @@ -234,7 +248,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: &Executor<CtxT>) -> ExecutionResult { + fn resolve_into_type(&self, type_name: &str, selection_set: Option<Vec<Selection>>, executor: &Executor<Self::Context>) -> ExecutionResult { if Self::name().unwrap() == type_name { Ok(self.resolve(selection_set, executor)) } else { @@ -246,7 +260,7 @@ pub trait GraphQLType<CtxT>: Sized { /// /// The default implementation panics. #[allow(unused_variables)] - fn concrete_type_name(&self, context: &CtxT) -> String { + fn concrete_type_name(&self, context: &Self::Context) -> String { panic!("concrete_type_name must be implemented by unions and interfaces"); } @@ -260,7 +274,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: &Executor<CtxT>) -> Value { + fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &Executor<Self::Context>) -> Value { if let Some(selection_set) = selection_set { let mut result = HashMap::new(); resolve_selection_set_into(self, selection_set, executor, &mut result); @@ -277,7 +291,7 @@ fn resolve_selection_set_into<T, CtxT>( selection_set: Vec<Selection>, executor: &Executor<CtxT>, result: &mut HashMap<String, Value>) - where T: GraphQLType<CtxT> + where T: GraphQLType<Context=CtxT> { let meta_type = executor.schema() .concrete_type_by_name(T::name().expect("Resolving named type's selection set")) diff --git a/src/types/containers.rs b/src/types/containers.rs index 2fcb0d4e..86e8f14c 100644 --- a/src/types/containers.rs +++ b/src/types/containers.rs @@ -5,12 +5,14 @@ use schema::meta::MetaType; use executor::{Executor, Registry, IntoFieldResult, FieldResult}; use types::base::{GraphQLType}; -impl<T, CtxT> GraphQLType<CtxT> for Option<T> where T: GraphQLType<CtxT> { +impl<T, CtxT> GraphQLType for Option<T> where T: GraphQLType<Context=CtxT> { + type Context = CtxT; + fn name() -> Option<&'static str> { None } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_nullable_type::<T>().into_meta() } @@ -50,12 +52,14 @@ impl<T> IntoFieldResult<Option<T>> for Option<T> { } -impl<T, CtxT> GraphQLType<CtxT> for Vec<T> where T: GraphQLType<CtxT> { +impl<T, CtxT> GraphQLType for Vec<T> where T: GraphQLType<Context=CtxT> { + type Context = CtxT; + fn name() -> Option<&'static str> { None } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_list_type::<T>().into_meta() } @@ -102,12 +106,14 @@ impl<T> IntoFieldResult<Vec<T>> for Vec<T> { } -impl<'a, T, CtxT> GraphQLType<CtxT> for &'a [T] where T: GraphQLType<CtxT> { +impl<'a, T, CtxT> GraphQLType for &'a [T] where T: GraphQLType<Context=CtxT> { + type Context = CtxT; + fn name() -> Option<&'static str> { None } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_list_type::<T>().into_meta() } diff --git a/src/types/pointers.rs b/src/types/pointers.rs index 7124945a..cf8d1b7d 100644 --- a/src/types/pointers.rs +++ b/src/types/pointers.rs @@ -5,12 +5,14 @@ use schema::meta::MetaType; use executor::{Executor, Registry, ExecutionResult, IntoFieldResult, FieldResult}; use types::base::{Arguments, GraphQLType}; -impl<T, CtxT> GraphQLType<CtxT> for Box<T> where T: GraphQLType<CtxT> { +impl<T, CtxT> GraphQLType for Box<T> where T: GraphQLType<Context=CtxT> { + type Context = CtxT; + fn name() -> Option<&'static str> { T::name() } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { T::meta(registry) } @@ -49,12 +51,14 @@ impl<T> IntoFieldResult<Box<T>> for Box<T> { } } -impl<'a, T, CtxT> GraphQLType<CtxT> for &'a T where T: GraphQLType<CtxT> { +impl<'a, T, CtxT> GraphQLType for &'a T where T: GraphQLType<Context=CtxT> { + type Context = CtxT; + fn name() -> Option<&'static str> { T::name() } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { T::meta(registry) } diff --git a/src/types/scalars.rs b/src/types/scalars.rs index c7eac471..8d5f6499 100644 --- a/src/types/scalars.rs +++ b/src/types/scalars.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use ast::{InputValue, Selection, FromInputValue, ToInputValue}; use value::Value; @@ -40,16 +42,18 @@ graphql_scalar!(String as "String" { }); -impl<'a, CtxT> GraphQLType<CtxT> for &'a str { +impl<'a> GraphQLType for &'a str { + type Context = (); + fn name() -> Option<&'static str> { Some("String") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_scalar_type::<String>().into_meta() } - fn resolve(&self, _: Option<Vec<Selection>>, _: &Executor<CtxT>) -> Value { + fn resolve(&self, _: Option<Vec<Selection>>, _: &Executor<Self::Context>) -> Value { Value::string(self) } } @@ -111,12 +115,14 @@ graphql_scalar!(f64 as "Float" { }); -impl<CtxT> GraphQLType<CtxT> for () { +impl GraphQLType for () { + type Context = (); + fn name() -> Option<&'static str> { Some("__Unit") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_scalar_type::<Self>().into_meta() } } @@ -132,3 +138,39 @@ impl IntoFieldResult<()> for () { Ok(self) } } + + +/// Utility type to define read-only schemas +/// +/// If you instantiate `RootNode` with this as the mutation, no mutation will be +/// generated for the schema. +pub struct EmptyMutation<T> { + phantom: PhantomData<T>, +} + +impl<T> EmptyMutation<T> { + /// Construct a new empty mutation + pub fn new() -> EmptyMutation<T> { + EmptyMutation { + phantom: PhantomData, + } + } +} + +impl<T> GraphQLType for EmptyMutation<T> { + type Context = T; + + fn name() -> Option<&'static str> { + Some("__EmptyMutation") + } + + fn meta(registry: &mut Registry) -> MetaType { + registry.build_object_type::<Self>()(&[]).into_meta() + } +} + +impl<T> IntoFieldResult<EmptyMutation<T>> for EmptyMutation<T> { + fn into(self) -> FieldResult<EmptyMutation<T>> { + Ok(self) + } +} diff --git a/src/validation/rules/overlapping_fields_can_be_merged.rs b/src/validation/rules/overlapping_fields_can_be_merged.rs index 88000c49..2011279f 100644 --- a/src/validation/rules/overlapping_fields_can_be_merged.rs +++ b/src/validation/rules/overlapping_fields_can_be_merged.rs @@ -1164,12 +1164,14 @@ mod tests { struct Node; struct QueryRoot; - impl<CtxT> GraphQLType<CtxT> for SomeBox { + impl GraphQLType for SomeBox { + type Context = (); + fn name() -> Option<&'static str> { Some("SomeBox") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_interface_type::<Self>()(&[ registry.field::<Option<SomeBox>>("deepBox"), registry.field::<Option<String>>("unrelatedField"), @@ -1178,12 +1180,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for StringBox { + impl GraphQLType for StringBox { + type Context = (); + fn name() -> Option<&'static str> { Some("StringBox") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<String>>("scalar"), registry.field::<Option<StringBox>>("deepBox"), @@ -1199,12 +1203,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for IntBox { + impl GraphQLType for IntBox { + type Context = (); + fn name() -> Option<&'static str> { Some("IntBox") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<i64>>("scalar"), registry.field::<Option<IntBox>>("deepBox"), @@ -1220,12 +1226,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for NonNullStringBox1 { + impl GraphQLType for NonNullStringBox1 { + type Context = (); + fn name() -> Option<&'static str> { Some("NonNullStringBox1") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_interface_type::<Self>()(&[ registry.field::<String>("scalar"), ]) @@ -1233,12 +1241,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for NonNullStringBox1Impl { + impl GraphQLType for NonNullStringBox1Impl { + type Context = (); + fn name() -> Option<&'static str> { Some("NonNullStringBox1Impl") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<String>("scalar"), registry.field::<Option<SomeBox>>("deepBox"), @@ -1252,12 +1262,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for NonNullStringBox2 { + impl GraphQLType for NonNullStringBox2 { + type Context = (); + fn name() -> Option<&'static str> { Some("NonNullStringBox2") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_interface_type::<Self>()(&[ registry.field::<String>("scalar"), ]) @@ -1265,12 +1277,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for NonNullStringBox2Impl { + impl GraphQLType for NonNullStringBox2Impl { + type Context = (); + fn name() -> Option<&'static str> { Some("NonNullStringBox2Impl") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<String>("scalar"), registry.field::<Option<SomeBox>>("deepBox"), @@ -1284,12 +1298,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for Node { + impl GraphQLType for Node { + type Context = (); + fn name() -> Option<&'static str> { Some("Node") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<ID>>("id"), registry.field::<Option<String>>("name"), @@ -1298,12 +1314,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for Edge { + impl GraphQLType for Edge { + type Context = (); + fn name() -> Option<&'static str> { Some("Edge") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<Node>>("node"), ]) @@ -1311,12 +1329,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for Connection { + impl GraphQLType for Connection { + type Context = (); + fn name() -> Option<&'static str> { Some("Connection") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<Vec<Option<Edge>>>>("edges"), ]) @@ -1324,12 +1344,14 @@ mod tests { } } - impl<CtxT> GraphQLType<CtxT> for QueryRoot { + impl GraphQLType for QueryRoot { + type Context = (); + fn name() -> Option<&'static str> { Some("QueryRoot") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.get_type::<IntBox>(); registry.get_type::<StringBox>(); registry.get_type::<NonNullStringBox1Impl>(); diff --git a/src/validation/test_harness.rs b/src/validation/test_harness.rs index 96277c17..40edc7e0 100644 --- a/src/validation/test_harness.rs +++ b/src/validation/test_harness.rs @@ -2,7 +2,7 @@ use parser::parse_document_source; use ast::{FromInputValue, InputValue}; use types::base::GraphQLType; use executor::Registry; -use types::scalars::ID; +use types::scalars::{EmptyMutation, ID}; use schema::model::{DirectiveType, DirectiveLocation, RootNode}; use schema::meta::{EnumValue, MetaType}; use validation::{Visitor, RuleError, ValidatorContext, MultiVisitor, visit}; @@ -51,12 +51,14 @@ struct ComplexInput { string_list_field: Option<Vec<Option<String>>>, } -impl<CtxT> GraphQLType<CtxT> for Being { +impl GraphQLType for Being { + type Context = (); + fn name() -> Option<&'static str> { Some("Being") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_interface_type::<Self>()(&[ registry.field::<Option<String>>("name") .argument(registry.arg::<Option<bool>>("surname")), @@ -65,12 +67,14 @@ impl<CtxT> GraphQLType<CtxT> for Being { } } -impl<CtxT> GraphQLType<CtxT> for Pet { +impl GraphQLType for Pet { + type Context = (); + fn name() -> Option<&'static str> { Some("Pet") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_interface_type::<Self>()(&[ registry.field::<Option<String>>("name") .argument(registry.arg::<Option<bool>>("surname")), @@ -79,12 +83,14 @@ impl<CtxT> GraphQLType<CtxT> for Pet { } } -impl<CtxT> GraphQLType<CtxT> for Canine { +impl GraphQLType for Canine { + type Context = (); + fn name() -> Option<&'static str> { Some("Canine") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_interface_type::<Self>()(&[ registry.field::<Option<String>>("name") .argument(registry.arg::<Option<bool>>("surname")), @@ -93,12 +99,14 @@ impl<CtxT> GraphQLType<CtxT> for Canine { } } -impl<CtxT> GraphQLType<CtxT> for DogCommand { +impl GraphQLType for DogCommand { + type Context = (); + fn name() -> Option<&'static str> { Some("DogCommand") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_enum_type::<Self>()(&[ EnumValue::new("SIT"), EnumValue::new("HEEL"), @@ -119,12 +127,14 @@ impl FromInputValue for DogCommand { } } -impl<CtxT> GraphQLType<CtxT> for Dog { +impl GraphQLType for Dog { + type Context = (); + fn name() -> Option<&'static str> { Some("Dog") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<String>>("name") .argument(registry.arg::<Option<bool>>("surname")), @@ -148,12 +158,14 @@ impl<CtxT> GraphQLType<CtxT> for Dog { } } -impl<CtxT> GraphQLType<CtxT> for FurColor { +impl GraphQLType for FurColor { + type Context = (); + fn name() -> Option<&'static str> { Some("FurColor") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_enum_type::<Self>()(&[ EnumValue::new("BROWN"), EnumValue::new("BLACK"), @@ -176,12 +188,14 @@ impl FromInputValue for FurColor { } } -impl<CtxT> GraphQLType<CtxT> for Cat { +impl GraphQLType for Cat { + type Context = (); + fn name() -> Option<&'static str> { Some("Cat") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<String>>("name") .argument(registry.arg::<Option<bool>>("surname")), @@ -198,12 +212,14 @@ impl<CtxT> GraphQLType<CtxT> for Cat { } } -impl<CtxT> GraphQLType<CtxT> for CatOrDog { +impl GraphQLType for CatOrDog { + type Context = (); + fn name() -> Option<&'static str> { Some("CatOrDog") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_union_type::<Self>()(&[ registry.get_type::<Cat>(), registry.get_type::<Dog>(), @@ -212,12 +228,14 @@ impl<CtxT> GraphQLType<CtxT> for CatOrDog { } } -impl<CtxT> GraphQLType<CtxT> for Intelligent { +impl GraphQLType for Intelligent { + type Context = (); + fn name() -> Option<&'static str> { Some("Intelligent") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_interface_type::<Self>()(&[ registry.field::<Option<i64>>("iq"), ]) @@ -225,12 +243,14 @@ impl<CtxT> GraphQLType<CtxT> for Intelligent { } } -impl<CtxT> GraphQLType<CtxT> for Human { +impl GraphQLType for Human { + type Context = (); + fn name() -> Option<&'static str> { Some("Human") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<String>>("name") .argument(registry.arg::<Option<bool>>("surname")), @@ -246,12 +266,14 @@ impl<CtxT> GraphQLType<CtxT> for Human { } } -impl<CtxT> GraphQLType<CtxT> for Alien { +impl GraphQLType for Alien { + type Context = (); + fn name() -> Option<&'static str> { Some("Alien") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<String>>("name") .argument(registry.arg::<Option<bool>>("surname")), @@ -266,12 +288,14 @@ impl<CtxT> GraphQLType<CtxT> for Alien { } } -impl<CtxT> GraphQLType<CtxT> for DogOrHuman { +impl GraphQLType for DogOrHuman { + type Context = (); + fn name() -> Option<&'static str> { Some("DogOrHuman") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_union_type::<Self>()(&[ registry.get_type::<Dog>(), registry.get_type::<Human>(), @@ -280,12 +304,14 @@ impl<CtxT> GraphQLType<CtxT> for DogOrHuman { } } -impl<CtxT> GraphQLType<CtxT> for HumanOrAlien { +impl GraphQLType for HumanOrAlien { + type Context = (); + fn name() -> Option<&'static str> { Some("HumanOrAlien") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_union_type::<Self>()(&[ registry.get_type::<Human>(), registry.get_type::<Alien>(), @@ -294,12 +320,14 @@ impl<CtxT> GraphQLType<CtxT> for HumanOrAlien { } } -impl<CtxT> GraphQLType<CtxT> for ComplexInput { +impl GraphQLType for ComplexInput { + type Context = (); + fn name() -> Option<&'static str> { Some("ComplexInput") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_input_object_type::<Self>()(&[ registry.arg::<bool>("requiredField"), registry.arg::<Option<i64>>("intField"), @@ -331,12 +359,14 @@ impl FromInputValue for ComplexInput { } } -impl<CtxT> GraphQLType<CtxT> for ComplicatedArgs { +impl GraphQLType for ComplicatedArgs { + type Context = (); + fn name() -> Option<&'static str> { Some("ComplicatedArgs") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<String>>("intArgField") .argument(registry.arg::<Option<i64>>("intArg")), @@ -372,12 +402,14 @@ impl<CtxT> GraphQLType<CtxT> for ComplicatedArgs { } } -impl<CtxT> GraphQLType<CtxT> for QueryRoot { +impl GraphQLType for QueryRoot { + type Context = (); + fn name() -> Option<&'static str> { Some("QueryRoot") } - fn meta(registry: &mut Registry<CtxT>) -> MetaType { + fn meta(registry: &mut Registry) -> MetaType { registry.build_object_type::<Self>()(&[ registry.field::<Option<Human>>("human") .argument(registry.arg::<Option<ID>>("id")), @@ -396,11 +428,11 @@ impl<CtxT> GraphQLType<CtxT> for QueryRoot { pub fn validate<'a, R, V, F>(r: R, q: &str, factory: F) -> Vec<RuleError> - where R: GraphQLType<()>, + where R: GraphQLType, V: Visitor<'a> + 'a, F: Fn() -> V { - let mut root = RootNode::<(), R, ()>::new(r, ()); + let mut root = RootNode::new(r, EmptyMutation::<()>::new()); root.schema.add_directive(DirectiveType::new("onQuery", &[DirectiveLocation::Query], &[])); root.schema.add_directive(DirectiveType::new("onMutation", &[DirectiveLocation::Mutation], &[])); @@ -429,7 +461,7 @@ pub fn expect_passes_rule<'a, V, F>(factory: F, q: &str) } pub fn expect_passes_rule_with_schema<'a, R, V, F>(r: R, factory: F, q: &str) - where R: GraphQLType<()>, + where R: GraphQLType, V: Visitor<'a> + 'a, F: Fn() -> V { @@ -449,7 +481,7 @@ pub fn expect_fails_rule<'a, V, F>(factory: F, q: &str, expected_errors: &[RuleE } pub fn expect_fails_rule_with_schema<'a, R, V, F>(r: R, factory: F, q: &str, expected_errors: &[RuleError]) - where R: GraphQLType<()>, + where R: GraphQLType, V: Visitor<'a> + 'a, F: Fn() -> V {