v0.8.0
This commit is contained in:
parent
b3974737fc
commit
b3340680ba
|
@ -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"
|
||||
|
|
22
README.md
22
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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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 {}
|
||||
|
|
133
src/context.rs
133
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<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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -27,4 +27,8 @@ impl GQLOutputValue for GQLEmptyMutation {
|
|||
}
|
||||
}
|
||||
|
||||
impl GQLObject for GQLEmptyMutation {}
|
||||
impl GQLObject for GQLEmptyMutation {
|
||||
fn is_empty() -> bool {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?,
|
||||
|
|
Loading…
Reference in New Issue