implemented query fragment

This commit is contained in:
sunli 2020-03-05 15:50:57 +08:00
parent 3346184f42
commit b3974737fc
5 changed files with 85 additions and 37 deletions

View File

@ -235,12 +235,9 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
resolvers.push(quote! {
if field.name.as_str() == #field_name {
#(#get_params)*
let obj = #resolve_obj;
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))?;
let name = field.alias.clone().unwrap_or_else(|| field.name.clone());
result.insert(name, value.into());
let value = #resolve_obj.resolve(&ctx_obj).await.map_err(|err| err.with_position(field.position))?;
result.insert(field.alias.clone().unwrap_or_else(|| field.name.clone()), value.into());
continue;
}
});
@ -279,29 +276,25 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
}
let mut result = #crate_name::serde_json::Map::<String, #crate_name::serde_json::Value>::new();
for selection in &ctx.items {
match selection {
#crate_name::graphql_parser::query::Selection::Field(field) => {
let ctx_field = ctx.with_item(field);
if ctx_field.is_skip_this()? {
continue;
}
if field.name.as_str() == "__typename" {
let name = field.alias.clone().unwrap_or_else(|| field.name.clone());
result.insert(name, #gql_typename.into());
continue;
}
if field.name.as_str() == "__schema" {
continue;
}
#(#resolvers)*
#crate_name::anyhow::bail!(#crate_name::QueryError::FieldNotFound {
field_name: field.name.clone(),
object: #gql_typename,
}.with_position(field.position));
}
_ => {}
for field in ctx.fields(&*ctx) {
let field = field?;
let ctx_field = ctx.with_item(field);
if ctx_field.is_skip_this()? {
continue;
}
if field.name.as_str() == "__typename" {
let name = field.alias.clone().unwrap_or_else(|| field.name.clone());
result.insert(name, #gql_typename.into());
continue;
}
if field.name.as_str() == "__schema" {
continue;
}
#(#resolvers)*
#crate_name::anyhow::bail!(#crate_name::QueryError::FieldNotFound {
field_name: field.name.clone(),
object: #gql_typename,
}.with_position(field.position));
}
Ok(#crate_name::serde_json::Value::Object(result))

View File

@ -1,7 +1,9 @@
use crate::registry::Registry;
use crate::{ErrorWithPosition, GQLInputValue, GQLType, QueryError, Result};
use fnv::FnvHasher;
use graphql_parser::query::{Field, SelectionSet, Value, VariableDefinition};
use graphql_parser::query::{
Field, FragmentDefinition, Selection, SelectionSet, Value, VariableDefinition,
};
use std::any::{Any, TypeId};
use std::collections::{BTreeMap, HashMap};
use std::hash::BuildHasherDefault;
@ -73,6 +75,7 @@ pub struct ContextBase<'a, T> {
pub(crate) variable_definitions: Option<&'a [VariableDefinition]>,
pub(crate) registry: &'a Registry,
pub(crate) data: &'a Data,
pub(crate) fragments: &'a HashMap<String, &'a FragmentDefinition>,
}
impl<'a, T> Deref for ContextBase<'a, T> {
@ -83,6 +86,41 @@ impl<'a, T> Deref for ContextBase<'a, T> {
}
}
pub struct FieldIter<'a> {
fragments: &'a HashMap<String, &'a FragmentDefinition>,
stack: Vec<std::slice::Iter<'a, Selection>>,
}
impl<'a> Iterator for FieldIter<'a> {
type Item = Result<&'a Field>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(it) = self.stack.last_mut() {
if let Some(selection) = it.next() {
match selection {
Selection::Field(field) => {
return Some(Ok(field));
}
Selection::FragmentSpread(fragment_spread) => {
if let Some(fragment) = self.fragments.get(&fragment_spread.fragment_name) {
self.stack.push(fragment.selection_set.items.iter());
} else {
return Some(Err(QueryError::UnknownFragment {
name: fragment_spread.fragment_name.clone(),
}
.into()));
}
}
Selection::InlineFragment(_) => {}
}
} else {
self.stack.pop();
}
}
None
}
}
impl<'a, T> ContextBase<'a, T> {
#[doc(hidden)]
pub fn with_item<R>(&self, item: R) -> ContextBase<'a, R> {
@ -92,6 +130,7 @@ impl<'a, T> ContextBase<'a, T> {
variable_definitions: self.variable_definitions,
registry: self.registry.clone(),
data: self.data,
fragments: self.fragments,
}
}
@ -101,6 +140,14 @@ impl<'a, T> ContextBase<'a, T> {
.get(&TypeId::of::<D>())
.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> {

View File

@ -59,6 +59,9 @@ pub enum QueryError {
#[error("Unknown directive \"{name}\".")]
UnknownDirective { name: String },
#[error("Unknown fragment \"{name}\".")]
UnknownFragment { name: String },
}
pub trait ErrorWithPosition {

View File

@ -133,7 +133,7 @@ impl<'a> __Type<'a> {
None
}
#[field]
#[field(name = "possibleTypes")]
async fn possible_types(&self) -> Option<Vec<__Type<'a>>> {
None
}

View File

@ -3,12 +3,12 @@ use crate::model::__DirectiveLocation;
use crate::registry::{Directive, InputValue, Registry};
use crate::types::QueryRoot;
use crate::{
ContextBase, ErrorWithPosition, GQLObject, GQLOutputValue, GQLType, QueryError,
QueryParseError, Result, Variables,
ContextBase, GQLObject, GQLOutputValue, GQLType, QueryError, QueryParseError, Result, Variables,
};
use graphql_parser::parse_query;
use graphql_parser::query::{Definition, OperationDefinition};
use std::any::Any;
use std::collections::HashMap;
pub struct Schema<Query, Mutation> {
query: QueryRoot<Query>,
@ -116,6 +116,13 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
{
let document =
parse_query(self.query_source).map_err(|err| QueryParseError(err.to_string()))?;
let mut fragments = HashMap::new();
for definition in &document.definitions {
if let Definition::Fragment(fragment) = definition {
fragments.insert(fragment.name.clone(), fragment);
}
}
for definition in &document.definitions {
match definition {
@ -127,6 +134,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
variable_definitions: None,
registry: &self.registry,
data: self.data,
fragments: &fragments,
};
return self.query.resolve(&ctx).await;
}
@ -141,6 +149,7 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
variable_definitions: Some(&query.variable_definitions),
registry: self.registry.clone(),
data: self.data,
fragments: &fragments,
};
return self.query.resolve(&ctx).await;
}
@ -155,16 +164,12 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
variable_definitions: Some(&mutation.variable_definitions),
registry: self.registry.clone(),
data: self.data,
fragments: &fragments,
};
return self.mutation.resolve(&ctx).await;
}
}
Definition::Operation(OperationDefinition::Subscription(subscription)) => {
anyhow::bail!(QueryError::NotSupported.with_position(subscription.position));
}
Definition::Fragment(fragment) => {
anyhow::bail!(QueryError::NotSupported.with_position(fragment.position));
}
_ => {}
}
}