This commit is contained in:
sunli 2020-03-05 17:06:14 +08:00
parent b3974737fc
commit b3340680ba
11 changed files with 116 additions and 90 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "async-graphql"
version = "0.7.0"
version = "0.8.0"
authors = ["sunli <scott_s829@163.com>"]
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"

View File

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

View File

@ -1,6 +1,6 @@
[package]
name = "async-graphql-derive"
version = "0.7.0"
version = "0.8.0"
authors = ["sunli <scott_s829@163.com>"]
edition = "2018"
description = "The GraphQL server library implemented by rust"

View File

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

View File

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

View File

@ -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<String, &'a FragmentDefinition>,
pub struct FieldIter<'a, T> {
ctx: &'a ContextBase<'a, T>,
stack: Vec<std::slice::Iter<'a, Selection>>,
}
impl<'a> Iterator for FieldIter<'a> {
impl<'a, T> Iterator for FieldIter<'a, T> {
type Item = Result<&'a Field>;
fn next(&mut self) -> Option<Self::Item> {
@ -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::<D>())
}
#[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<Value> {
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<T: GQLInputValue, F: FnOnce() -> Value>(
&self,
name: &str,
default: Option<F>,
) -> Result<T> {
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<bool> {
for directive in &self.directives {
pub fn is_skip(&self, directives: &[Directive]) -> Result<bool> {
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<T: GQLInputValue, F: FnOnce() -> Value>(
&self,
name: &str,
default: Option<F>,
) -> Result<T> {
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)
}
}
}
}

View File

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

View File

@ -130,7 +130,11 @@ impl<'a> __Type<'a> {
#[field]
async fn interfaces(&self) -> Option<Vec<__Type<'a>>> {
None
if let TypeDetail::Simple(Type::Object { .. }) = &self.detail {
Some(vec![])
} else {
None
}
}
#[field(name = "possibleTypes")]

View File

@ -52,13 +52,19 @@ impl<Query: GQLObject, Mutation: GQLObject> Schema<Query, Mutation> {
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,

View File

@ -27,4 +27,8 @@ impl GQLOutputValue for GQLEmptyMutation {
}
}
impl GQLObject for GQLEmptyMutation {}
impl GQLObject for GQLEmptyMutation {
fn is_empty() -> bool {
return true;
}
}

View File

@ -6,7 +6,7 @@ use std::borrow::Cow;
pub struct QueryRoot<T> {
pub inner: T,
pub query_type: String,
pub mutation_type: String,
pub mutation_type: Option<String>,
}
impl<T: GQLType> GQLType for QueryRoot<T> {
@ -34,7 +34,7 @@ impl<T: GQLOutputValue + Send + Sync> GQLOutputValue for QueryRoot<T> {
__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?,