diff --git a/async-graphql-derive/src/enum.rs b/async-graphql-derive/src/enum.rs index 600455d9..499a38f8 100644 --- a/async-graphql-derive/src/enum.rs +++ b/async-graphql-derive/src/enum.rs @@ -101,10 +101,6 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result Option { #crate_name::GQLEnum::parse_enum(value) } - - fn parse_from_json(value: &#crate_name::serde_json::Value) -> Option { - #crate_name::GQLEnum::parse_json_enum(value) - } } #[#crate_name::async_trait::async_trait] diff --git a/async-graphql-derive/src/input_object.rs b/async-graphql-derive/src/input_object.rs index c64f45d1..515f3265 100644 --- a/async-graphql-derive/src/input_object.rs +++ b/async-graphql-derive/src/input_object.rs @@ -41,7 +41,6 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result< .unwrap_or_else(|| quote! {None}); let mut get_fields = Vec::new(); - let mut get_json_fields = Vec::new(); let mut fields = Vec::new(); let mut schema_fields = Vec::new(); @@ -83,25 +82,6 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result< }); } - if let Some(default) = &field_args.default { - let default_repr = build_value_repr(&crate_name, default); - get_json_fields.push(quote! { - let #ident:#ty = match obj.get(#name) { - None => { - let default_value = #default_repr; - #crate_name::GQLInputValue::parse(&default_value)? - } - Some(value) => { - #crate_name::GQLInputValue::parse_from_json(&value)? - } - }; - }); - } else { - get_json_fields.push(quote! { - let #ident:#ty = #crate_name::GQLInputValue::parse_from_json(&obj.get(#name).unwrap_or(&#crate_name::serde_json::Value::Null))?; - }); - } - fields.push(ident); schema_fields.push(quote! { #crate_name::registry::InputValue { @@ -141,16 +121,6 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result< None } } - - fn parse_from_json(value: &#crate_name::serde_json::Value) -> Option { - use #crate_name::GQLType; - if let #crate_name::serde_json::Value::Object(obj) = value { - #(#get_json_fields)* - Some(Self { #(#fields),* }) - } else { - None - } - } } impl #crate_name::GQLInputObject for #ident {} diff --git a/examples/helloworld.rs b/examples/helloworld.rs index c1c7685f..2b53a7bb 100644 --- a/examples/helloworld.rs +++ b/examples/helloworld.rs @@ -43,16 +43,16 @@ impl MyObjFields for MyObj { #[async_std::main] async fn main() { let schema = async_graphql::Schema::::new(); - for _ in 0..1000 { - let res = schema - .query( - MyObj { value: 100 }, - async_graphql::GQLEmptyMutation, - "{ a b c __schema { types { kind name description fields(includeDeprecated: false) { name args { name defaultValue } } } } }", - ) - .execute() - .await - .unwrap(); - } - // serde_json::to_writer_pretty(std::io::stdout(), &res).unwrap(); + let vars = async_graphql::Variables::parse_from_json(b"{\"myvar\": 999}").unwrap(); + let res = schema + .query( + MyObj { value: 100 }, + async_graphql::GQLEmptyMutation, + "query($myvar:Int) { a b(v:$myvar) }", + ) + .variables(&vars) + .execute() + .await + .unwrap(); + serde_json::to_writer_pretty(std::io::stdout(), &res).unwrap(); } diff --git a/src/base.rs b/src/base.rs index 302ba204..0e71a88b 100644 --- a/src/base.rs +++ b/src/base.rs @@ -16,7 +16,6 @@ pub trait GQLType { #[doc(hidden)] pub trait GQLInputValue: GQLType + Sized { fn parse(value: &Value) -> Option; - fn parse_from_json(value: &serde_json::Value) -> Option; } #[doc(hidden)] @@ -37,7 +36,6 @@ pub trait Scalar: Sized + Send { None } fn parse(value: &Value) -> Option; - fn parse_from_json(value: &serde_json::Value) -> Option; fn to_json(&self) -> Result; } @@ -58,10 +56,6 @@ impl GQLInputValue for T { fn parse(value: &Value) -> Option { T::parse(value) } - - fn parse_from_json(value: &serde_json::Value) -> Option { - T::parse_from_json(value) - } } #[async_trait::async_trait] diff --git a/src/context.rs b/src/context.rs index 3daeae9d..71fb5b12 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,23 +1,55 @@ use crate::registry::Registry; use crate::{ErrorWithPosition, GQLInputValue, QueryError, Result}; use fnv::FnvHasher; -use graphql_parser::query::{Field, SelectionSet, Value}; +use graphql_parser::query::{Field, SelectionSet, Value, VariableDefinition}; use std::any::{Any, TypeId}; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::hash::BuildHasherDefault; use std::ops::{Deref, DerefMut}; #[derive(Default)] -pub struct Variables(HashMap); +pub struct Variables(BTreeMap); impl Deref for Variables { - type Target = HashMap; + type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.0 } } +impl Variables { + pub fn parse_from_json(data: &[u8]) -> Result { + let value = serde_json::from_slice(data)?; + let gql_value = json_value_to_gql_value(value); + if let Value::Object(obj) = gql_value { + Ok(Variables(obj)) + } else { + Ok(Default::default()) + } + } +} + +fn json_value_to_gql_value(value: serde_json::Value) -> Value { + match value { + serde_json::Value::Null => Value::Null, + serde_json::Value::Bool(n) => Value::Boolean(n), + serde_json::Value::Number(n) if n.is_f64() => Value::Float(n.as_f64().unwrap()), + serde_json::Value::Number(n) => Value::Int((n.as_i64().unwrap() as i32).into()), + serde_json::Value::String(s) => Value::String(s), + serde_json::Value::Array(ls) => Value::List( + ls.into_iter() + .map(|value| json_value_to_gql_value(value)) + .collect(), + ), + serde_json::Value::Object(obj) => Value::Object( + obj.into_iter() + .map(|(name, value)| (name, json_value_to_gql_value(value))) + .collect(), + ), + } +} + impl DerefMut for Variables { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 @@ -44,6 +76,7 @@ pub struct ContextBase<'a, T> { pub(crate) item: T, pub(crate) data: Option<&'a Data>, pub(crate) variables: Option<&'a Variables>, + pub(crate) variable_definitions: Option<&'a [VariableDefinition]>, pub(crate) registry: &'a Registry, } @@ -62,6 +95,7 @@ impl<'a, T> ContextBase<'a, T> { item, data: self.data, variables: self.variables, + variable_definitions: self.variable_definitions, registry: self.registry.clone(), } } @@ -76,35 +110,40 @@ impl<'a, T> ContextBase<'a, T> { } impl<'a> ContextBase<'a, &'a Field> { + fn resolve_input_value(&self, value: Value) -> Result { + if let Value::Variable(var_name) = value { + let def = self + .variable_definitions + .and_then(|defs| defs.iter().find(|def| def.name == var_name.as_str())); + if let Some(def) = def { + if let Some(var_value) = self.variables.map(|vars| vars.get(&def.name)).flatten() { + return Ok(var_value.clone()); + } + } + return Err(QueryError::VarNotDefined { + var_name: var_name.clone(), + } + .into()); + } else { + Ok(value) + } + } + #[doc(hidden)] pub fn param_value Value>( &self, name: &str, default: Option, ) -> Result { - let value = self + match self .arguments .iter() .find(|(n, _)| n == name) .map(|(_, v)| v) - .cloned(); - - if let Some(Value::Variable(var_name)) = &value { - if let Some(vars) = &self.variables { - if let Some(var_value) = vars.get(&*var_name).cloned() { - let res = GQLInputValue::parse_from_json(&var_value).ok_or_else(|| { - QueryError::ExpectedJsonType { - expect: T::qualified_type_name(), - actual: var_value, - } - .with_position(self.item.position) - })?; - return Ok(res); - } - } - - if let Some(default) = default { - let value = default(); + .cloned() + { + Some(value) => { + let value = self.resolve_input_value(value)?; let res = GQLInputValue::parse(&value).ok_or_else(|| { QueryError::ExpectedType { expect: T::qualified_type_name(), @@ -112,28 +151,30 @@ impl<'a> ContextBase<'a, &'a Field> { } .with_position(self.item.position) })?; - return Ok(res); + Ok(res) } - - return Err(QueryError::VarNotDefined { - var_name: var_name.clone(), + None if default.is_some() => { + let default = default.unwrap(); + let value = default(); + let res = GQLInputValue::parse(&value).ok_or_else(|| { + QueryError::ExpectedType { + expect: T::qualified_type_name(), + actual: value.clone(), + } + .with_position(self.item.position) + })?; + Ok(res) } - .into()); - }; - - let value = if let (Some(default), None) = (default, &value) { - default() - } else { - value.unwrap_or(Value::Null) - }; - - let res = GQLInputValue::parse(&value).ok_or_else(|| { - QueryError::ExpectedType { - expect: T::qualified_type_name(), - actual: value, + None => { + let res = GQLInputValue::parse(&Value::Null).ok_or_else(|| { + QueryError::ExpectedType { + expect: T::qualified_type_name(), + actual: Value::Null, + } + .with_position(self.item.position) + })?; + Ok(res) } - .with_position(self.item.position) - })?; - Ok(res) + } } } diff --git a/src/scalars/bool.rs b/src/scalars/bool.rs index cbf1e9b7..0a1a7551 100644 --- a/src/scalars/bool.rs +++ b/src/scalars/bool.rs @@ -16,13 +16,6 @@ impl Scalar for bool { } } - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::Bool(n) => Some(*n), - _ => None, - } - } - fn to_json(&self) -> Result { Ok((*self).into()) } diff --git a/src/scalars/datetime.rs b/src/scalars/datetime.rs index b7784f73..8f8f4e13 100644 --- a/src/scalars/datetime.rs +++ b/src/scalars/datetime.rs @@ -13,13 +13,6 @@ impl Scalar for DateTime { } } - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::String(s) => Some(Utc.datetime_from_str(&s, "%+").ok()?), - _ => None, - } - } - fn to_json(&self) -> Result { Ok(self.to_rfc3339().into()) } diff --git a/src/scalars/floats.rs b/src/scalars/floats.rs index 5752f3f8..60fad35c 100644 --- a/src/scalars/floats.rs +++ b/src/scalars/floats.rs @@ -20,13 +20,6 @@ macro_rules! impl_float_scalars { } } - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::Number(n) => Some(n.as_f64().unwrap() as Self), - _ => None - } - } - fn to_json(&self) -> Result { Ok((*self).into()) } diff --git a/src/scalars/id.rs b/src/scalars/id.rs index 0b8b3caf..8d9ca0e5 100644 --- a/src/scalars/id.rs +++ b/src/scalars/id.rs @@ -31,14 +31,6 @@ impl Scalar for ID { } } - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::Number(n) if n.is_i64() => Some(ID(n.as_i64().unwrap().to_string())), - serde_json::Value::String(s) => Some(ID(s.clone())), - _ => None, - } - } - fn to_json(&self) -> Result { Ok(self.0.clone().into()) } diff --git a/src/scalars/integers.rs b/src/scalars/integers.rs index def161e7..e1fa63b6 100644 --- a/src/scalars/integers.rs +++ b/src/scalars/integers.rs @@ -19,14 +19,6 @@ macro_rules! impl_integer_scalars { } } - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::Number(n) if n.is_i64() => Some(n.as_i64().unwrap() as Self), - serde_json::Value::Number(n) => Some(n.as_f64().unwrap() as Self), - _ => None, - } - } - fn to_json(&self) -> Result { Ok((*self).into()) } diff --git a/src/scalars/string.rs b/src/scalars/string.rs index 5e2f51d1..cc92851d 100644 --- a/src/scalars/string.rs +++ b/src/scalars/string.rs @@ -20,13 +20,6 @@ impl Scalar for String { } } - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::String(s) => Some(s.clone()), - _ => None, - } - } - fn to_json(&self) -> Result { Ok(self.clone().into()) } diff --git a/src/scalars/uuid.rs b/src/scalars/uuid.rs index 70164d95..acd39703 100644 --- a/src/scalars/uuid.rs +++ b/src/scalars/uuid.rs @@ -13,13 +13,6 @@ impl Scalar for Uuid { } } - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::String(s) => Some(Uuid::parse_str(&s).ok()?), - _ => None, - } - } - fn to_json(&self) -> Result { Ok(self.to_string().into()) } diff --git a/src/schema.rs b/src/schema.rs index 79f319e6..ee334e92 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -96,6 +96,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> { item: selection_set, data: self.data.as_deref(), variables: self.variables.as_deref(), + variable_definitions: None, registry: &self.registry, }; return self.query.resolve(&ctx).await; @@ -109,6 +110,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> { item: &query.selection_set, data: self.data.as_deref(), variables: self.variables.as_deref(), + variable_definitions: Some(&query.variable_definitions), registry: self.registry.clone(), }; return self.query.resolve(&ctx).await; @@ -122,6 +124,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> { item: &mutation.selection_set, data: self.data.as_deref(), variables: self.variables.as_deref(), + variable_definitions: Some(&mutation.variable_definitions), registry: self.registry.clone(), }; return self.mutation.resolve(&ctx).await; diff --git a/src/types/enum.rs b/src/types/enum.rs index 63ee0911..29f7c567 100644 --- a/src/types/enum.rs +++ b/src/types/enum.rs @@ -13,33 +13,21 @@ pub trait GQLEnum: GQLType + Sized + Eq + Send + Copy + Sized + 'static { fn items() -> &'static [GQLEnumItem]; fn parse_enum(value: &Value) -> Option { - match value { - Value::Enum(s) => { - let items = Self::items(); - for item in items { - if item.name == s { - return Some(item.value); - } - } - } - _ => {} - } - None - } + let value = match value { + Value::Enum(s) => Some(s.as_str()), + Value::String(s) => Some(s.as_str()), + _ => None, + }; - fn parse_json_enum(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::String(s) => { - let items = Self::items(); - for item in items { - if item.name == s { - return Some(item.value); - } + value.and_then(|value| { + let items = Self::items(); + for item in items { + if item.name == value { + return Some(item.value); } } - _ => {} - } - None + None + }) } fn resolve_enum(&self) -> Result { diff --git a/src/types/list.rs b/src/types/list.rs index 8a250ca6..966d833d 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -24,19 +24,6 @@ impl GQLInputValue for Vec { _ => None, } } - - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::Array(values) => { - let mut result = Vec::new(); - for value in values { - result.push(GQLInputValue::parse_from_json(value)?); - } - Some(result) - } - _ => None, - } - } } #[async_trait::async_trait] diff --git a/src/types/optional.rs b/src/types/optional.rs index 865d12f1..497ce9f3 100644 --- a/src/types/optional.rs +++ b/src/types/optional.rs @@ -22,13 +22,6 @@ impl GQLInputValue for Option { _ => Some(GQLInputValue::parse(value)?), } } - - fn parse_from_json(value: &serde_json::Value) -> Option { - match value { - serde_json::Value::Null => Some(None), - _ => Some(GQLInputValue::parse_from_json(value)?), - } - } } #[async_trait::async_trait]