variables supported
This commit is contained in:
parent
fdf3c1d360
commit
781aabdff0
|
@ -51,7 +51,7 @@
|
|||
- [ ] Fragments
|
||||
- [ ] Inline fragments
|
||||
- [X] Operation name
|
||||
- [ ] Variables
|
||||
- [X] Variables
|
||||
- [ ] Directives
|
||||
- [ ] Schema
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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)*
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
27
src/enum.rs
27
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<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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod context;
|
||||
mod r#enum;
|
||||
|
|
|
@ -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())
|
||||
|
|
28
src/type.rs
28
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<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]
|
||||
|
|
16
src/uuid.rs
16
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<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())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue