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> {
#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]

View File

@ -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<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 {}

View File

@ -43,16 +43,16 @@ impl MyObjFields for MyObj {
#[async_std::main]
async fn main() {
let schema = async_graphql::Schema::<MyObj, async_graphql::GQLEmptyMutation>::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();
}

View File

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

View File

@ -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<String, serde_json::Value>);
pub struct Variables(BTreeMap<String, Value>);
impl Deref for Variables {
type Target = HashMap<String, serde_json::Value>;
type Target = BTreeMap<String, Value>;
fn deref(&self) -> &Self::Target {
&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 {
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<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)]
pub fn param_value<T: GQLInputValue, F: FnOnce() -> Value>(
&self,
name: &str,
default: Option<F>,
) -> Result<T> {
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)
}
}
}

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> {
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> {
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> {
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> {
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> {
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> {
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> {
Ok(self.to_string().into())
}

View File

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

View File

@ -13,33 +13,21 @@ pub trait GQLEnum: GQLType + Sized + Eq + Send + Copy + Sized + 'static {
fn items() -> &'static [GQLEnumItem<Self>];
fn parse_enum(value: &Value) -> Option<Self> {
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<Self> {
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<serde_json::Value> {

View File

@ -24,19 +24,6 @@ impl<T: GQLInputValue> GQLInputValue for Vec<T> {
_ => 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]

View File

@ -22,13 +22,6 @@ impl<T: GQLInputValue> GQLInputValue for Option<T> {
_ => 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]