diff --git a/README.md b/README.md index 3b527528..8c537fb8 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ - [ ] Fragments - [ ] Inline fragments - [X] Operation name - - [ ] Variables + - [X] Variables - [ ] Directives - [ ] Schema diff --git a/async-graphql-derive/src/enum.rs b/async-graphql-derive/src/enum.rs index 22bd84ed..9b60f1eb 100644 --- a/async-graphql-derive/src/enum.rs +++ b/async-graphql-derive/src/enum.rs @@ -67,9 +67,13 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result Result { + fn parse(value: async_graphql::Value) -> Result { Self::parse_enum(value) } + + fn parse_from_json(value: async_graphql::serde_json::Value) -> Result { + Self::parse_json_enum(value) + } } #[async_graphql::async_trait::async_trait] diff --git a/async-graphql-derive/src/input_object.rs b/async-graphql-derive/src/input_object.rs index 39624ab1..4d8c7759 100644 --- a/async-graphql-derive/src/input_object.rs +++ b/async-graphql-derive/src/input_object.rs @@ -16,7 +16,9 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result Result Result async_graphql::Result { + if let async_graphql::serde_json::Value::Object(mut obj) = value { + #(#get_json_fields)* + Ok(Self { #(#fields),* }) + } else { + Err(async_graphql::QueryError::ExpectedJsonType { + expect: Self::type_name(), + actual: value, + }.into()) + } + } } impl async_graphql::GQLInputObject for #ident {} diff --git a/async-graphql-derive/src/object.rs b/async-graphql-derive/src/object.rs index a71a4e93..ffc3593c 100644 --- a/async-graphql-derive/src/object.rs +++ b/async-graphql-derive/src/object.rs @@ -62,7 +62,8 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result Result { let ctx_field = ctx.with_item(field); if field.name.as_str() == "__typename" { - result.insert("__typename".to_string(), #gql_typename.into()); + let name = field.alias.clone().unwrap_or_else(|| field.name.clone()); + result.insert(name, #gql_typename.into()); continue; } #(#resolvers)* diff --git a/src/context.rs b/src/context.rs index 3d227516..3ace3f84 100644 --- a/src/context.rs +++ b/src/context.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use std::hash::BuildHasherDefault; use std::ops::{Deref, DerefMut}; -#[derive(Serialize, Deserialize, Default)] +#[derive(Default)] pub struct Variables(HashMap); impl Deref for Variables { @@ -78,28 +78,25 @@ impl<'a> Context<'a, &'a Field> { .iter() .find(|(n, _)| n == name) .map(|(_, v)| v) - .cloned() - .unwrap_or(Value::Null); - let value = match (value, &self.variables) { - (Value::Variable(name), Some(vars)) => match vars.get(&name).cloned() { - Some(value) => value, - None => { - return Err(QueryError::VarNotDefined { - var_name: name.clone(), - } - .into()); + .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) + .map_err(|err| err.with_position(self.item.position))?; + return Ok(res); } - }, - (Value::Variable(name), None) => { - return Err(QueryError::VarNotDefined { - var_name: name.clone(), - } - .into()); } - (value, _) => value, + + return Err(QueryError::VarNotDefined { + var_name: var_name.clone(), + } + .into()); }; - let res = - GQLInputValue::parse(value).map_err(|err| err.with_position(self.item.position))?; + + let res = GQLInputValue::parse(value.unwrap_or(Value::Null)) + .map_err(|err| err.with_position(self.item.position))?; Ok(res) } } diff --git a/src/datetime.rs b/src/datetime.rs index 0147cf3e..02530a08 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -1,4 +1,5 @@ -use crate::{QueryError, Scalar, Result, Value}; +use crate::{QueryError, Result, Scalar, Value}; +use anyhow::Error; use chrono::{DateTime, TimeZone, Utc}; impl Scalar for DateTime { @@ -19,6 +20,19 @@ impl Scalar for DateTime { } } + fn parse_from_json(value: serde_json::Value) -> Result { + match value { + serde_json::Value::String(s) => Ok(Utc.datetime_from_str(&s, "%+")?), + _ => { + return Err(QueryError::ExpectedJsonType { + expect: Self::type_name().to_string(), + actual: value, + } + .into()) + } + } + } + fn into_json(self) -> Result { Ok(self.to_rfc3339().into()) } diff --git a/src/enum.rs b/src/enum.rs index cd594dd5..27a9baa7 100644 --- a/src/enum.rs +++ b/src/enum.rs @@ -1,4 +1,4 @@ -use crate::{QueryError, GQLType, Result}; +use crate::{GQLType, QueryError, Result}; use graphql_parser::query::Value; #[doc(hidden)] @@ -38,6 +38,31 @@ pub trait GQLEnum: GQLType + Sized + Eq + Send + Copy + Sized + 'static { } } + fn parse_json_enum(value: serde_json::Value) -> Result { + match value { + serde_json::Value::String(s) => { + let items = Self::items(); + for item in items { + if item.name == s { + return Ok(item.value); + } + } + Err(QueryError::InvalidEnumValue { + enum_type: Self::type_name(), + value: s, + } + .into()) + } + _ => { + return Err(QueryError::ExpectedJsonType { + expect: Self::type_name(), + actual: value, + } + .into()) + } + } + } + fn resolve_enum(self) -> Result { let items = Self::items(); for item in items { diff --git a/src/error.rs b/src/error.rs index a0eb58b3..50ae0231 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,6 +15,12 @@ pub enum QueryError { #[error("Expected type \"{expect}\", found {actual}.")] ExpectedType { expect: String, actual: Value }, + #[error("Expected type \"{expect}\", found {actual}.")] + ExpectedJsonType { + expect: String, + actual: serde_json::Value, + }, + #[error("Cannot query field \"{field_name}\" on type \"{object}\".")] FieldNotFound { field_name: String, diff --git a/src/lib.rs b/src/lib.rs index 325f8bf9..bf011b00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,8 +32,6 @@ #[macro_use] extern crate thiserror; -#[macro_use] -extern crate serde_derive; mod context; mod r#enum; diff --git a/src/scalar.rs b/src/scalar.rs index e71abd4e..15c2d13d 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -1,10 +1,12 @@ use crate::r#type::{GQLInputValue, GQLOutputValue, GQLType}; use crate::{ContextSelectionSet, QueryError, Result}; +use anyhow::Error; use graphql_parser::query::Value; pub trait Scalar: Sized + Send { fn type_name() -> &'static str; fn parse(value: Value) -> Result; + fn parse_from_json(value: serde_json::Value) -> Result; fn into_json(self) -> Result; } @@ -18,6 +20,10 @@ impl GQLInputValue for T { fn parse(value: Value) -> Result { T::parse(value) } + + fn parse_from_json(value: serde_json::Value) -> Result { + T::parse_from_json(value) + } } #[async_trait::async_trait] @@ -48,6 +54,20 @@ macro_rules! impl_integer_scalars { } } + fn parse_from_json(value: serde_json::Value) -> Result { + match value { + serde_json::Value::Number(n) if n.is_i64() => Ok(n.as_i64().unwrap() as Self), + serde_json::Value::Number(n) => Ok(n.as_f64().unwrap() as Self), + _ => { + return Err(QueryError::ExpectedJsonType { + expect: ::type_name().to_string(), + actual: value, + } + .into()) + } + } + } + fn into_json(self) -> Result { Ok(self.into()) } @@ -80,6 +100,19 @@ macro_rules! impl_float_scalars { } } + fn parse_from_json(value: serde_json::Value) -> Result { + match value { + serde_json::Value::Number(n) => Ok(n.as_f64().unwrap() as Self), + _ => { + return Err(QueryError::ExpectedJsonType { + expect: ::type_name().to_string(), + actual: value, + } + .into()) + } + } + } + fn into_json(self) -> Result { Ok(self.into()) } @@ -100,7 +133,20 @@ impl Scalar for String { Value::String(s) => Ok(s), _ => { return Err(QueryError::ExpectedType { - expect: ::type_name().to_string(), + expect: ::type_name().to_string(), + actual: value, + } + .into()) + } + } + } + + fn parse_from_json(value: serde_json::Value) -> Result { + match value { + serde_json::Value::String(s) => Ok(s), + _ => { + return Err(QueryError::ExpectedJsonType { + expect: ::type_name().to_string(), actual: value, } .into()) @@ -123,7 +169,20 @@ impl Scalar for bool { Value::Boolean(n) => Ok(n), _ => { return Err(QueryError::ExpectedType { - expect: ::type_name().to_string(), + expect: ::type_name().to_string(), + actual: value, + } + .into()) + } + } + } + + fn parse_from_json(value: serde_json::Value) -> Result { + match value { + serde_json::Value::Bool(n) => Ok(n), + _ => { + return Err(QueryError::ExpectedJsonType { + expect: ::type_name().to_string(), actual: value, } .into()) diff --git a/src/type.rs b/src/type.rs index 662e9a0c..2405739c 100644 --- a/src/type.rs +++ b/src/type.rs @@ -1,4 +1,5 @@ use crate::{ContextSelectionSet, ErrorWithPosition, QueryError, Result}; +use anyhow::Error; use graphql_parser::query::Value; #[doc(hidden)] @@ -9,6 +10,7 @@ pub trait GQLType { #[doc(hidden)] pub trait GQLInputValue: GQLType + Sized { fn parse(value: Value) -> Result; + fn parse_from_json(value: serde_json::Value) -> Result; } #[doc(hidden)] @@ -42,6 +44,25 @@ impl GQLInputValue for Vec { } } } + + fn parse_from_json(value: serde_json::Value) -> Result { + match value { + serde_json::Value::Array(values) => { + let mut result = Vec::new(); + for value in values { + result.push(GQLInputValue::parse_from_json(value)?); + } + Ok(result) + } + _ => { + return Err(QueryError::ExpectedJsonType { + expect: Self::type_name(), + actual: value, + } + .into()) + } + } + } } #[async_trait::async_trait] @@ -68,6 +89,13 @@ impl GQLInputValue for Option { _ => Ok(Some(GQLInputValue::parse(value)?)), } } + + fn parse_from_json(value: serde_json::Value) -> Result { + match value { + serde_json::Value::Null => Ok(None), + _ => Ok(Some(GQLInputValue::parse_from_json(value)?)), + } + } } #[async_trait::async_trait] diff --git a/src/uuid.rs b/src/uuid.rs index a71f9dfa..af7d2775 100644 --- a/src/uuid.rs +++ b/src/uuid.rs @@ -1,4 +1,5 @@ -use crate::{QueryError, Scalar, Result, Value}; +use crate::{QueryError, Result, Scalar, Value}; +use anyhow::Error; use uuid::Uuid; impl Scalar for Uuid { @@ -19,6 +20,19 @@ impl Scalar for Uuid { } } + fn parse_from_json(value: serde_json::Value) -> Result { + match value { + serde_json::Value::String(s) => Ok(Uuid::parse_str(&s)?), + _ => { + return Err(QueryError::ExpectedJsonType { + expect: Self::type_name().to_string(), + actual: value, + } + .into()) + } + } + } + fn into_json(self) -> Result { Ok(self.to_string().into()) }