support variables

This commit is contained in:
sunli 2020-03-04 11:51:42 +08:00
parent 602f6a656f
commit b7a65b6784
16 changed files with 111 additions and 190 deletions

View File

@ -101,10 +101,6 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
fn parse(value: &#crate_name::Value) -> Option<Self> { fn parse(value: &#crate_name::Value) -> Option<Self> {
#crate_name::GQLEnum::parse_enum(value) #crate_name::GQLEnum::parse_enum(value)
} }
fn parse_from_json(value: &#crate_name::serde_json::Value) -> Option<Self> {
#crate_name::GQLEnum::parse_json_enum(value)
}
} }
#[#crate_name::async_trait::async_trait] #[#crate_name::async_trait::async_trait]

View File

@ -41,7 +41,6 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
.unwrap_or_else(|| quote! {None}); .unwrap_or_else(|| quote! {None});
let mut get_fields = Vec::new(); let mut get_fields = Vec::new();
let mut get_json_fields = Vec::new();
let mut fields = Vec::new(); let mut fields = Vec::new();
let mut schema_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); fields.push(ident);
schema_fields.push(quote! { schema_fields.push(quote! {
#crate_name::registry::InputValue { #crate_name::registry::InputValue {
@ -141,16 +121,6 @@ pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<
None None
} }
} }
fn parse_from_json(value: &#crate_name::serde_json::Value) -> Option<Self> {
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 {} impl #crate_name::GQLInputObject for #ident {}

View File

@ -43,16 +43,16 @@ impl MyObjFields for MyObj {
#[async_std::main] #[async_std::main]
async fn main() { async fn main() {
let schema = async_graphql::Schema::<MyObj, async_graphql::GQLEmptyMutation>::new(); let schema = async_graphql::Schema::<MyObj, async_graphql::GQLEmptyMutation>::new();
for _ in 0..1000 { let vars = async_graphql::Variables::parse_from_json(b"{\"myvar\": 999}").unwrap();
let res = schema let res = schema
.query( .query(
MyObj { value: 100 }, MyObj { value: 100 },
async_graphql::GQLEmptyMutation, async_graphql::GQLEmptyMutation,
"{ a b c __schema { types { kind name description fields(includeDeprecated: false) { name args { name defaultValue } } } } }", "query($myvar:Int) { a b(v:$myvar) }",
) )
.execute() .variables(&vars)
.await .execute()
.unwrap(); .await
} .unwrap();
// serde_json::to_writer_pretty(std::io::stdout(), &res).unwrap(); serde_json::to_writer_pretty(std::io::stdout(), &res).unwrap();
} }

View File

@ -16,7 +16,6 @@ pub trait GQLType {
#[doc(hidden)] #[doc(hidden)]
pub trait GQLInputValue: GQLType + Sized { pub trait GQLInputValue: GQLType + Sized {
fn parse(value: &Value) -> Option<Self>; fn parse(value: &Value) -> Option<Self>;
fn parse_from_json(value: &serde_json::Value) -> Option<Self>;
} }
#[doc(hidden)] #[doc(hidden)]
@ -37,7 +36,6 @@ pub trait Scalar: Sized + Send {
None None
} }
fn parse(value: &Value) -> Option<Self>; fn parse(value: &Value) -> Option<Self>;
fn parse_from_json(value: &serde_json::Value) -> Option<Self>;
fn to_json(&self) -> Result<serde_json::Value>; fn to_json(&self) -> Result<serde_json::Value>;
} }
@ -58,10 +56,6 @@ impl<T: Scalar> GQLInputValue for T {
fn parse(value: &Value) -> Option<Self> { fn parse(value: &Value) -> Option<Self> {
T::parse(value) T::parse(value)
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
T::parse_from_json(value)
}
} }
#[async_trait::async_trait] #[async_trait::async_trait]

View File

@ -1,23 +1,55 @@
use crate::registry::Registry; use crate::registry::Registry;
use crate::{ErrorWithPosition, GQLInputValue, QueryError, Result}; use crate::{ErrorWithPosition, GQLInputValue, QueryError, Result};
use fnv::FnvHasher; 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::any::{Any, TypeId};
use std::collections::HashMap; use std::collections::{BTreeMap, HashMap};
use std::hash::BuildHasherDefault; use std::hash::BuildHasherDefault;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
#[derive(Default)] #[derive(Default)]
pub struct Variables(HashMap<String, serde_json::Value>); pub struct Variables(BTreeMap<String, Value>);
impl Deref for Variables { impl Deref for Variables {
type Target = HashMap<String, serde_json::Value>; type Target = BTreeMap<String, Value>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
impl Variables {
pub fn parse_from_json(data: &[u8]) -> Result<Self> {
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 { impl DerefMut for Variables {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0 &mut self.0
@ -44,6 +76,7 @@ pub struct ContextBase<'a, T> {
pub(crate) item: T, pub(crate) item: T,
pub(crate) data: Option<&'a Data>, pub(crate) data: Option<&'a Data>,
pub(crate) variables: Option<&'a Variables>, pub(crate) variables: Option<&'a Variables>,
pub(crate) variable_definitions: Option<&'a [VariableDefinition]>,
pub(crate) registry: &'a Registry, pub(crate) registry: &'a Registry,
} }
@ -62,6 +95,7 @@ impl<'a, T> ContextBase<'a, T> {
item, item,
data: self.data, data: self.data,
variables: self.variables, variables: self.variables,
variable_definitions: self.variable_definitions,
registry: self.registry.clone(), registry: self.registry.clone(),
} }
} }
@ -76,35 +110,40 @@ impl<'a, T> ContextBase<'a, T> {
} }
impl<'a> ContextBase<'a, &'a Field> { impl<'a> ContextBase<'a, &'a Field> {
fn resolve_input_value(&self, value: Value) -> Result<Value> {
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)] #[doc(hidden)]
pub fn param_value<T: GQLInputValue, F: FnOnce() -> Value>( pub fn param_value<T: GQLInputValue, F: FnOnce() -> Value>(
&self, &self,
name: &str, name: &str,
default: Option<F>, default: Option<F>,
) -> Result<T> { ) -> Result<T> {
let value = self match self
.arguments .arguments
.iter() .iter()
.find(|(n, _)| n == name) .find(|(n, _)| n == name)
.map(|(_, v)| v) .map(|(_, v)| v)
.cloned(); .cloned()
{
if let Some(Value::Variable(var_name)) = &value { Some(value) => {
if let Some(vars) = &self.variables { let value = self.resolve_input_value(value)?;
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();
let res = GQLInputValue::parse(&value).ok_or_else(|| { let res = GQLInputValue::parse(&value).ok_or_else(|| {
QueryError::ExpectedType { QueryError::ExpectedType {
expect: T::qualified_type_name(), expect: T::qualified_type_name(),
@ -112,28 +151,30 @@ impl<'a> ContextBase<'a, &'a Field> {
} }
.with_position(self.item.position) .with_position(self.item.position)
})?; })?;
return Ok(res); Ok(res)
} }
None if default.is_some() => {
return Err(QueryError::VarNotDefined { let default = default.unwrap();
var_name: var_name.clone(), 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()); None => {
}; let res = GQLInputValue::parse(&Value::Null).ok_or_else(|| {
QueryError::ExpectedType {
let value = if let (Some(default), None) = (default, &value) { expect: T::qualified_type_name(),
default() actual: Value::Null,
} else { }
value.unwrap_or(Value::Null) .with_position(self.item.position)
}; })?;
Ok(res)
let res = GQLInputValue::parse(&value).ok_or_else(|| {
QueryError::ExpectedType {
expect: T::qualified_type_name(),
actual: value,
} }
.with_position(self.item.position) }
})?;
Ok(res)
} }
} }

View File

@ -16,13 +16,6 @@ impl Scalar for bool {
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
match value {
serde_json::Value::Bool(n) => Some(*n),
_ => None,
}
}
fn to_json(&self) -> Result<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into()) Ok((*self).into())
} }

View File

@ -13,13 +13,6 @@ impl Scalar for DateTime<Utc> {
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
match value {
serde_json::Value::String(s) => Some(Utc.datetime_from_str(&s, "%+").ok()?),
_ => None,
}
}
fn to_json(&self) -> Result<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_rfc3339().into()) Ok(self.to_rfc3339().into())
} }

View File

@ -20,13 +20,6 @@ macro_rules! impl_float_scalars {
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
match value {
serde_json::Value::Number(n) => Some(n.as_f64().unwrap() as Self),
_ => None
}
}
fn to_json(&self) -> Result<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into()) Ok((*self).into())
} }

View File

@ -31,14 +31,6 @@ impl Scalar for ID {
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
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<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.0.clone().into()) Ok(self.0.clone().into())
} }

View File

@ -19,14 +19,6 @@ macro_rules! impl_integer_scalars {
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
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<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
Ok((*self).into()) Ok((*self).into())
} }

View File

@ -20,13 +20,6 @@ impl Scalar for String {
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
match value {
serde_json::Value::String(s) => Some(s.clone()),
_ => None,
}
}
fn to_json(&self) -> Result<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.clone().into()) Ok(self.clone().into())
} }

View File

@ -13,13 +13,6 @@ impl Scalar for Uuid {
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
match value {
serde_json::Value::String(s) => Some(Uuid::parse_str(&s).ok()?),
_ => None,
}
}
fn to_json(&self) -> Result<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
Ok(self.to_string().into()) Ok(self.to_string().into())
} }

View File

@ -96,6 +96,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
item: selection_set, item: selection_set,
data: self.data.as_deref(), data: self.data.as_deref(),
variables: self.variables.as_deref(), variables: self.variables.as_deref(),
variable_definitions: None,
registry: &self.registry, registry: &self.registry,
}; };
return self.query.resolve(&ctx).await; return self.query.resolve(&ctx).await;
@ -109,6 +110,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
item: &query.selection_set, item: &query.selection_set,
data: self.data.as_deref(), data: self.data.as_deref(),
variables: self.variables.as_deref(), variables: self.variables.as_deref(),
variable_definitions: Some(&query.variable_definitions),
registry: self.registry.clone(), registry: self.registry.clone(),
}; };
return self.query.resolve(&ctx).await; return self.query.resolve(&ctx).await;
@ -122,6 +124,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
item: &mutation.selection_set, item: &mutation.selection_set,
data: self.data.as_deref(), data: self.data.as_deref(),
variables: self.variables.as_deref(), variables: self.variables.as_deref(),
variable_definitions: Some(&mutation.variable_definitions),
registry: self.registry.clone(), registry: self.registry.clone(),
}; };
return self.mutation.resolve(&ctx).await; return self.mutation.resolve(&ctx).await;

View File

@ -13,33 +13,21 @@ pub trait GQLEnum: GQLType + Sized + Eq + Send + Copy + Sized + 'static {
fn items() -> &'static [GQLEnumItem<Self>]; fn items() -> &'static [GQLEnumItem<Self>];
fn parse_enum(value: &Value) -> Option<Self> { fn parse_enum(value: &Value) -> Option<Self> {
match value { let value = match value {
Value::Enum(s) => { Value::Enum(s) => Some(s.as_str()),
let items = Self::items(); Value::String(s) => Some(s.as_str()),
for item in items { _ => None,
if item.name == s { };
return Some(item.value);
}
}
}
_ => {}
}
None
}
fn parse_json_enum(value: &serde_json::Value) -> Option<Self> { value.and_then(|value| {
match value { let items = Self::items();
serde_json::Value::String(s) => { for item in items {
let items = Self::items(); if item.name == value {
for item in items { return Some(item.value);
if item.name == s {
return Some(item.value);
}
} }
} }
_ => {} None
} })
None
} }
fn resolve_enum(&self) -> Result<serde_json::Value> { fn resolve_enum(&self) -> Result<serde_json::Value> {

View File

@ -24,19 +24,6 @@ impl<T: GQLInputValue> GQLInputValue for Vec<T> {
_ => None, _ => None,
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
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] #[async_trait::async_trait]

View File

@ -22,13 +22,6 @@ impl<T: GQLInputValue> GQLInputValue for Option<T> {
_ => Some(GQLInputValue::parse(value)?), _ => Some(GQLInputValue::parse(value)?),
} }
} }
fn parse_from_json(value: &serde_json::Value) -> Option<Self> {
match value {
serde_json::Value::Null => Some(None),
_ => Some(GQLInputValue::parse_from_json(value)?),
}
}
} }
#[async_trait::async_trait] #[async_trait::async_trait]