support variables
This commit is contained in:
parent
602f6a656f
commit
b7a65b6784
|
@ -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]
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
127
src/context.rs
127
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<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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue