variables supported
This commit is contained in:
parent
fdf3c1d360
commit
781aabdff0
|
@ -51,7 +51,7 @@
|
||||||
- [ ] Fragments
|
- [ ] Fragments
|
||||||
- [ ] Inline fragments
|
- [ ] Inline fragments
|
||||||
- [X] Operation name
|
- [X] Operation name
|
||||||
- [ ] Variables
|
- [X] Variables
|
||||||
- [ ] Directives
|
- [ ] Directives
|
||||||
- [ ] Schema
|
- [ ] Schema
|
||||||
|
|
||||||
|
|
|
@ -67,9 +67,13 @@ pub fn generate(enum_args: &args::Enum, input: &DeriveInput) -> Result<TokenStre
|
||||||
}
|
}
|
||||||
|
|
||||||
impl async_graphql::GQLInputValue for #ident {
|
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)
|
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]
|
#[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());
|
.unwrap_or_else(|| ident.to_string());
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
for field in &s.fields {
|
for field in &s.fields {
|
||||||
let field_args = args::InputField::parse(&field.attrs)?;
|
let field_args = args::InputField::parse(&field.attrs)?;
|
||||||
let ident = field.ident.as_ref().unwrap();
|
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! {
|
get_fields.push(quote! {
|
||||||
let #ident:#ty = async_graphql::GQLInputValue::parse(obj.remove(#name).unwrap_or(async_graphql::Value::Null))?;
|
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);
|
fields.push(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +54,18 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
||||||
}.into())
|
}.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 {}
|
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 ctx_obj = ctx_field.with_item(&field.selection_set);
|
||||||
let value = obj.resolve(&ctx_obj).await.
|
let value = obj.resolve(&ctx_obj).await.
|
||||||
map_err(|err| err.with_position(field.position))?;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -105,7 +106,8 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
||||||
async_graphql::graphql_parser::query::Selection::Field(field) => {
|
async_graphql::graphql_parser::query::Selection::Field(field) => {
|
||||||
let ctx_field = ctx.with_item(field);
|
let ctx_field = ctx.with_item(field);
|
||||||
if field.name.as_str() == "__typename" {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
#(#resolvers)*
|
#(#resolvers)*
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::collections::HashMap;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Default)]
|
||||||
pub struct Variables(HashMap<String, serde_json::Value>);
|
pub struct Variables(HashMap<String, serde_json::Value>);
|
||||||
|
|
||||||
impl Deref for Variables {
|
impl Deref for Variables {
|
||||||
|
@ -78,28 +78,25 @@ impl<'a> Context<'a, &'a Field> {
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(n, _)| n == name)
|
.find(|(n, _)| n == name)
|
||||||
.map(|(_, v)| v)
|
.map(|(_, v)| v)
|
||||||
.cloned()
|
.cloned();
|
||||||
.unwrap_or(Value::Null);
|
|
||||||
let value = match (value, &self.variables) {
|
if let Some(Value::Variable(var_name)) = &value {
|
||||||
(Value::Variable(name), Some(vars)) => match vars.get(&name).cloned() {
|
if let Some(vars) = &self.variables {
|
||||||
Some(value) => value,
|
if let Some(var_value) = vars.get(&*var_name).cloned() {
|
||||||
None => {
|
let res = GQLInputValue::parse_from_json(var_value)
|
||||||
|
.map_err(|err| err.with_position(self.item.position))?;
|
||||||
|
return Ok(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Err(QueryError::VarNotDefined {
|
return Err(QueryError::VarNotDefined {
|
||||||
var_name: name.clone(),
|
var_name: var_name.clone(),
|
||||||
}
|
}
|
||||||
.into());
|
.into());
|
||||||
}
|
|
||||||
},
|
|
||||||
(Value::Variable(name), None) => {
|
|
||||||
return Err(QueryError::VarNotDefined {
|
|
||||||
var_name: name.clone(),
|
|
||||||
}
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
(value, _) => value,
|
|
||||||
};
|
};
|
||||||
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)
|
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};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
|
||||||
impl Scalar for DateTime<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> {
|
fn into_json(self) -> Result<serde_json::Value> {
|
||||||
Ok(self.to_rfc3339().into())
|
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;
|
use graphql_parser::query::Value;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[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> {
|
fn resolve_enum(self) -> Result<serde_json::Value> {
|
||||||
let items = Self::items();
|
let items = Self::items();
|
||||||
for item in items {
|
for item in items {
|
||||||
|
|
|
@ -15,6 +15,12 @@ pub enum QueryError {
|
||||||
#[error("Expected type \"{expect}\", found {actual}.")]
|
#[error("Expected type \"{expect}\", found {actual}.")]
|
||||||
ExpectedType { expect: String, actual: Value },
|
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}\".")]
|
#[error("Cannot query field \"{field_name}\" on type \"{object}\".")]
|
||||||
FieldNotFound {
|
FieldNotFound {
|
||||||
field_name: String,
|
field_name: String,
|
||||||
|
|
|
@ -32,8 +32,6 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate thiserror;
|
extern crate thiserror;
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
mod r#enum;
|
mod r#enum;
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use crate::r#type::{GQLInputValue, GQLOutputValue, GQLType};
|
use crate::r#type::{GQLInputValue, GQLOutputValue, GQLType};
|
||||||
use crate::{ContextSelectionSet, QueryError, Result};
|
use crate::{ContextSelectionSet, QueryError, Result};
|
||||||
|
use anyhow::Error;
|
||||||
use graphql_parser::query::Value;
|
use graphql_parser::query::Value;
|
||||||
|
|
||||||
pub trait Scalar: Sized + Send {
|
pub trait Scalar: Sized + Send {
|
||||||
fn type_name() -> &'static str;
|
fn type_name() -> &'static str;
|
||||||
fn parse(value: Value) -> Result<Self>;
|
fn parse(value: Value) -> Result<Self>;
|
||||||
|
fn parse_from_json(value: serde_json::Value) -> Result<Self>;
|
||||||
fn into_json(self) -> Result<serde_json::Value>;
|
fn into_json(self) -> Result<serde_json::Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +20,10 @@ impl<T: Scalar> GQLInputValue for T {
|
||||||
fn parse(value: Value) -> Result<Self> {
|
fn parse(value: Value) -> Result<Self> {
|
||||||
T::parse(value)
|
T::parse(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||||
|
T::parse_from_json(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[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> {
|
fn into_json(self) -> Result<serde_json::Value> {
|
||||||
Ok(self.into())
|
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> {
|
fn into_json(self) -> Result<serde_json::Value> {
|
||||||
Ok(self.into())
|
Ok(self.into())
|
||||||
}
|
}
|
||||||
|
@ -100,7 +133,20 @@ impl Scalar for String {
|
||||||
Value::String(s) => Ok(s),
|
Value::String(s) => Ok(s),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(QueryError::ExpectedType {
|
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,
|
actual: value,
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
|
@ -123,7 +169,20 @@ impl Scalar for bool {
|
||||||
Value::Boolean(n) => Ok(n),
|
Value::Boolean(n) => Ok(n),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(QueryError::ExpectedType {
|
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,
|
actual: value,
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
|
|
28
src/type.rs
28
src/type.rs
|
@ -1,4 +1,5 @@
|
||||||
use crate::{ContextSelectionSet, ErrorWithPosition, QueryError, Result};
|
use crate::{ContextSelectionSet, ErrorWithPosition, QueryError, Result};
|
||||||
|
use anyhow::Error;
|
||||||
use graphql_parser::query::Value;
|
use graphql_parser::query::Value;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -9,6 +10,7 @@ pub trait GQLType {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait GQLInputValue: GQLType + Sized {
|
pub trait GQLInputValue: GQLType + Sized {
|
||||||
fn parse(value: Value) -> Result<Self>;
|
fn parse(value: Value) -> Result<Self>;
|
||||||
|
fn parse_from_json(value: serde_json::Value) -> Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[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]
|
#[async_trait::async_trait]
|
||||||
|
@ -68,6 +89,13 @@ impl<T: GQLInputValue> GQLInputValue for Option<T> {
|
||||||
_ => Ok(Some(GQLInputValue::parse(value)?)),
|
_ => 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]
|
#[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;
|
use uuid::Uuid;
|
||||||
|
|
||||||
impl Scalar for 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> {
|
fn into_json(self) -> Result<serde_json::Value> {
|
||||||
Ok(self.to_string().into())
|
Ok(self.to_string().into())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user