variables supported

This commit is contained in:
sunli 2020-03-02 00:52:05 +08:00
parent fdf3c1d360
commit 781aabdff0
12 changed files with 195 additions and 31 deletions

View File

@ -51,7 +51,7 @@
- [ ] Fragments
- [ ] Inline fragments
- [X] Operation name
- [ ] Variables
- [X] Variables
- [ ] Directives
- [ ] Schema

View File

@ -67,9 +67,13 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
}
impl async_graphql::GQLInputValue for #ident {
fn parse(value: graphql_parser::query::Value) -> Result<Self> {
fn parse(value: async_graphql::Value) -> Result<Self> {
Self::parse_enum(value)
}
fn parse_from_json(value: async_graphql::serde_json::Value) -> Result<Self> {
Self::parse_json_enum(value)
}
}
#[async_graphql::async_trait::async_trait]

View File

@ -16,7 +16,9 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
.unwrap_or_else(|| ident.to_string());
let mut get_fields = Vec::new();
let mut get_json_fields = Vec::new();
let mut fields = Vec::new();
for field in &s.fields {
let field_args = args::InputField::parse(&field.attrs)?;
let ident = field.ident.as_ref().unwrap();
@ -25,6 +27,9 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
get_fields.push(quote! {
let #ident:#ty = async_graphql::GQLInputValue::parse(obj.remove(#name).unwrap_or(async_graphql::Value::Null))?;
});
get_json_fields.push(quote! {
let #ident:#ty = async_graphql::GQLInputValue::parse_from_json(obj.remove(#name).unwrap_or(async_graphql::serde_json::Value::Null))?;
});
fields.push(ident);
}
@ -49,6 +54,18 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
}.into())
}
}
fn parse_from_json(value: async_graphql::serde_json::Value) -> async_graphql::Result<Self> {
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 {}

View File

@ -62,7 +62,8 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
let ctx_obj = ctx_field.with_item(&field.selection_set);
let value = obj.resolve(&ctx_obj).await.
map_err(|err| err.with_position(field.position))?;
result.insert(#field_name.to_string(), value.into());
let name = field.alias.clone().unwrap_or_else(|| field.name.clone());
result.insert(name, value.into());
continue;
}
});
@ -105,7 +106,8 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
async_graphql::graphql_parser::query::Selection::Field(field) => {
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)*

View File

@ -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<String, serde_json::Value>);
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)
}
}

View File

@ -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<Utc> {
@ -19,6 +20,19 @@ impl Scalar for DateTime<Utc> {
}
}
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
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<serde_json::Value> {
Ok(self.to_rfc3339().into())
}

View File

@ -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<Self> {
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<serde_json::Value> {
let items = Self::items();
for item in items {

View File

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

View File

@ -32,8 +32,6 @@
#[macro_use]
extern crate thiserror;
#[macro_use]
extern crate serde_derive;
mod context;
mod r#enum;

View File

@ -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<Self>;
fn parse_from_json(value: serde_json::Value) -> Result<Self>;
fn into_json(self) -> Result<serde_json::Value>;
}
@ -18,6 +20,10 @@ impl<T: Scalar> GQLInputValue for T {
fn parse(value: Value) -> Result<Self> {
T::parse(value)
}
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
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<Self> {
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: <Self as GQLType>::type_name().to_string(),
actual: value,
}
.into())
}
}
}
fn into_json(self) -> Result<serde_json::Value> {
Ok(self.into())
}
@ -80,6 +100,19 @@ macro_rules! impl_float_scalars {
}
}
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
match value {
serde_json::Value::Number(n) => Ok(n.as_f64().unwrap() as Self),
_ => {
return Err(QueryError::ExpectedJsonType {
expect: <Self as GQLType>::type_name().to_string(),
actual: value,
}
.into())
}
}
}
fn into_json(self) -> Result<serde_json::Value> {
Ok(self.into())
}
@ -100,7 +133,20 @@ impl Scalar for String {
Value::String(s) => Ok(s),
_ => {
return Err(QueryError::ExpectedType {
expect: <Self as Scalar>::type_name().to_string(),
expect: <Self as GQLType>::type_name().to_string(),
actual: value,
}
.into())
}
}
}
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
match value {
serde_json::Value::String(s) => Ok(s),
_ => {
return Err(QueryError::ExpectedJsonType {
expect: <Self as GQLType>::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: <Self as Scalar>::type_name().to_string(),
expect: <Self as GQLType>::type_name().to_string(),
actual: value,
}
.into())
}
}
}
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
match value {
serde_json::Value::Bool(n) => Ok(n),
_ => {
return Err(QueryError::ExpectedJsonType {
expect: <Self as GQLType>::type_name().to_string(),
actual: value,
}
.into())

View File

@ -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<Self>;
fn parse_from_json(value: serde_json::Value) -> Result<Self>;
}
#[doc(hidden)]
@ -42,6 +44,25 @@ impl<T: GQLInputValue> GQLInputValue for Vec<T> {
}
}
}
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
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<T: GQLInputValue> GQLInputValue for Option<T> {
_ => Ok(Some(GQLInputValue::parse(value)?)),
}
}
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
match value {
serde_json::Value::Null => Ok(None),
_ => Ok(Some(GQLInputValue::parse_from_json(value)?)),
}
}
}
#[async_trait::async_trait]

View File

@ -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<Self> {
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<serde_json::Value> {
Ok(self.to_string().into())
}