From b3340680ba03ce84fdef0f079d159554094f21e9 Mon Sep 17 00:00:00 2001 From: sunli Date: Thu, 5 Mar 2020 17:06:14 +0800 Subject: [PATCH] v0.8.0 --- Cargo.toml | 4 +- README.md | 22 +++-- async-graphql-derive/Cargo.toml | 2 +- async-graphql-derive/src/object.rs | 2 +- src/base.rs | 6 +- src/context.rs | 133 +++++++++++++++-------------- src/model/schema.rs | 11 +-- src/model/type.rs | 6 +- src/schema.rs | 10 ++- src/types/empty_mutation.rs | 6 +- src/types/query_root.rs | 4 +- 11 files changed, 116 insertions(+), 90 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3359cdd1..cd65cab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-graphql" -version = "0.7.0" +version = "0.8.0" authors = ["sunli "] edition = "2018" description = "The GraphQL server library implemented by rust" @@ -17,7 +17,7 @@ readme = "README.md" default = ["chrono", "uuid"] [dependencies] -async-graphql-derive = { path = "async-graphql-derive", version = "0.7.0" } +async-graphql-derive = { path = "async-graphql-derive", version = "0.8.0" } graphql-parser = "0.2.3" anyhow = "1.0.26" thiserror = "1.0.11" diff --git a/README.md b/README.md index cea3c1ad..d10b0c1d 100644 --- a/README.md +++ b/README.md @@ -60,32 +60,30 @@ - [X] Deprecated flag - [ ] Interface - [ ] Union -- [ ] Query +- [X] Query - [X] Fields - [X] Arguments - [X] Default value - [X] Deprecated flag - [X] Alias - - [ ] Fragments - - [ ] Inline fragments + - [X] Fragments + - [X] Inline fragments - [X] Operation name - [X] Variables - [X] Default value - [X] Parse value - - [ ] Check type - - [ ] Directives + - [X] Directives - [X] @include - [X] FIELD - - [ ] FRAGMENT_SPREAD - - [ ] INLINE_FRAGMENT + - [X] FRAGMENT_SPREAD + - [X] INLINE_FRAGMENT - [X] @skip - [X] FIELD - - [ ] FRAGMENT_SPREAD - - [ ] INLINE_FRAGMENT - - [ ] Custom Directive + - [X] FRAGMENT_SPREAD + - [X] INLINE_FRAGMENT - [X] Schema - - [ ] Validation rules -- [ ] +- [ ] Validation rules +- [ ] Integration examples - [X] Actix-web - [ ] Hyper - [ ] Tide diff --git a/async-graphql-derive/Cargo.toml b/async-graphql-derive/Cargo.toml index f78d1cc7..57a20ebf 100644 --- a/async-graphql-derive/Cargo.toml +++ b/async-graphql-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-graphql-derive" -version = "0.7.0" +version = "0.8.0" authors = ["sunli "] edition = "2018" description = "The GraphQL server library implemented by rust" diff --git a/async-graphql-derive/src/object.rs b/async-graphql-derive/src/object.rs index f0cf9596..b9f665d3 100644 --- a/async-graphql-derive/src/object.rs +++ b/async-graphql-derive/src/object.rs @@ -279,7 +279,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< for field in ctx.fields(&*ctx) { let field = field?; let ctx_field = ctx.with_item(field); - if ctx_field.is_skip_this()? { + if ctx_field.is_skip(&field.directives)? { continue; } if field.name.as_str() == "__typename" { diff --git a/src/base.rs b/src/base.rs index 3e80be6f..e219858b 100644 --- a/src/base.rs +++ b/src/base.rs @@ -25,7 +25,11 @@ pub trait GQLOutputValue: GQLType { } #[doc(hidden)] -pub trait GQLObject: GQLOutputValue {} +pub trait GQLObject: GQLOutputValue { + fn is_empty() -> bool { + return false; + } +} #[doc(hidden)] pub trait GQLInputObject: GQLInputValue {} diff --git a/src/context.rs b/src/context.rs index 7b78ec00..ff4165ed 100644 --- a/src/context.rs +++ b/src/context.rs @@ -2,7 +2,7 @@ use crate::registry::Registry; use crate::{ErrorWithPosition, GQLInputValue, GQLType, QueryError, Result}; use fnv::FnvHasher; use graphql_parser::query::{ - Field, FragmentDefinition, Selection, SelectionSet, Value, VariableDefinition, + Directive, Field, FragmentDefinition, Selection, SelectionSet, Value, VariableDefinition, }; use std::any::{Any, TypeId}; use std::collections::{BTreeMap, HashMap}; @@ -86,12 +86,12 @@ impl<'a, T> Deref for ContextBase<'a, T> { } } -pub struct FieldIter<'a> { - fragments: &'a HashMap, +pub struct FieldIter<'a, T> { + ctx: &'a ContextBase<'a, T>, stack: Vec>, } -impl<'a> Iterator for FieldIter<'a> { +impl<'a, T> Iterator for FieldIter<'a, T> { type Item = Result<&'a Field>; fn next(&mut self) -> Option { @@ -102,7 +102,16 @@ impl<'a> Iterator for FieldIter<'a> { return Some(Ok(field)); } Selection::FragmentSpread(fragment_spread) => { - if let Some(fragment) = self.fragments.get(&fragment_spread.fragment_name) { + let skip = match self.ctx.is_skip(&fragment_spread.directives) { + Ok(skip) => skip, + Err(err) => return Some(Err(err)), + }; + if skip { + continue; + } + if let Some(fragment) = + self.ctx.fragments.get(&fragment_spread.fragment_name) + { self.stack.push(fragment.selection_set.items.iter()); } else { return Some(Err(QueryError::UnknownFragment { @@ -141,16 +150,6 @@ impl<'a, T> ContextBase<'a, T> { .and_then(|d| d.downcast_ref::()) } - #[doc(hidden)] - pub fn fields(&self, selection_set: &'a SelectionSet) -> FieldIter { - FieldIter { - fragments: self.fragments, - stack: vec![selection_set.items.iter()], - } - } -} - -impl<'a> ContextBase<'a, &'a Field> { fn resolve_input_value(&self, value: Value) -> Result { if let Value::Variable(var_name) = value { let def = self @@ -173,57 +172,16 @@ impl<'a> ContextBase<'a, &'a Field> { } #[doc(hidden)] - pub fn param_value Value>( - &self, - name: &str, - default: Option, - ) -> Result { - match self - .arguments - .iter() - .find(|(n, _)| n == name) - .map(|(_, v)| v) - .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(), - actual: value, - } - .with_position(self.item.position) - })?; - Ok(res) - } - 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) - } - 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) - } + pub fn fields(&'a self, selection_set: &'a SelectionSet) -> FieldIter<'a, T> { + FieldIter { + ctx: self, + stack: vec![selection_set.items.iter()], } } #[doc(hidden)] - pub fn is_skip_this(&self) -> Result { - for directive in &self.directives { + pub fn is_skip(&self, directives: &[Directive]) -> Result { + for directive in directives { if directive.name == "skip" { if let Some(value) = directive .arguments @@ -290,3 +248,54 @@ impl<'a> ContextBase<'a, &'a Field> { Ok(false) } } + +impl<'a> ContextBase<'a, &'a Field> { + #[doc(hidden)] + pub fn param_value Value>( + &self, + name: &str, + default: Option, + ) -> Result { + match self + .arguments + .iter() + .find(|(n, _)| n == name) + .map(|(_, v)| v) + .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(), + actual: value, + } + .with_position(self.item.position) + })?; + Ok(res) + } + 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) + } + 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) + } + } + } +} diff --git a/src/model/schema.rs b/src/model/schema.rs index d4e81a79..11c1ab3d 100644 --- a/src/model/schema.rs +++ b/src/model/schema.rs @@ -5,7 +5,7 @@ use async_graphql_derive::Object; pub struct __Schema<'a> { pub registry: &'a registry::Registry, pub query_type: &'a str, - pub mutation_type: &'a str, + pub mutation_type: Option<&'a str>, } #[Object( @@ -35,10 +35,11 @@ impl<'a> __Schema<'a> { desc = "If this server supports mutation, the type that mutation operations will be rooted at." )] async fn mutation_type(&self) -> Option<__Type<'a>> { - Some(__Type::new_simple( - self.registry, - &self.registry.types[self.mutation_type], - )) + if let Some(ty) = self.mutation_type { + Some(__Type::new_simple(self.registry, &self.registry.types[ty])) + } else { + None + } } #[field( diff --git a/src/model/type.rs b/src/model/type.rs index 0f7b5579..53507227 100644 --- a/src/model/type.rs +++ b/src/model/type.rs @@ -130,7 +130,11 @@ impl<'a> __Type<'a> { #[field] async fn interfaces(&self) -> Option>> { - None + if let TypeDetail::Simple(Type::Object { .. }) = &self.detail { + Some(vec![]) + } else { + None + } } #[field(name = "possibleTypes")] diff --git a/src/schema.rs b/src/schema.rs index 28b9457b..b9885fc0 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -52,13 +52,19 @@ impl Schema { String::create_type_info(&mut registry); Query::create_type_info(&mut registry); - Mutation::create_type_info(&mut registry); + if !Mutation::is_empty() { + Mutation::create_type_info(&mut registry); + } Self { query: QueryRoot { inner: query, query_type: Query::type_name().to_string(), - mutation_type: Mutation::type_name().to_string(), + mutation_type: if !Mutation::is_empty() { + Some(Mutation::type_name().to_string()) + } else { + None + }, }, mutation, registry, diff --git a/src/types/empty_mutation.rs b/src/types/empty_mutation.rs index 135b24c4..75d68b5a 100644 --- a/src/types/empty_mutation.rs +++ b/src/types/empty_mutation.rs @@ -27,4 +27,8 @@ impl GQLOutputValue for GQLEmptyMutation { } } -impl GQLObject for GQLEmptyMutation {} +impl GQLObject for GQLEmptyMutation { + fn is_empty() -> bool { + return true; + } +} diff --git a/src/types/query_root.rs b/src/types/query_root.rs index f08efbaa..86bebb28 100644 --- a/src/types/query_root.rs +++ b/src/types/query_root.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; pub struct QueryRoot { pub inner: T, pub query_type: String, - pub mutation_type: String, + pub mutation_type: Option, } impl GQLType for QueryRoot { @@ -34,7 +34,7 @@ impl GQLOutputValue for QueryRoot { __Schema { registry: &ctx.registry, query_type: &self.query_type, - mutation_type: &self.mutation_type, + mutation_type: self.mutation_type.as_deref(), } .resolve(&ctx_obj) .await?,