Added support for inline fragment

This commit is contained in:
sunli 2020-03-07 10:39:55 +08:00
parent 40edb87fe0
commit b4edae7981
8 changed files with 95 additions and 13 deletions

View File

@ -37,6 +37,7 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
.unwrap_or_else(|| quote! {None});
let mut registry_types = Vec::new();
let mut possible_types = Vec::new();
let mut inline_fragment_resolvers = Vec::new();
for field in &fields.unnamed {
if let Type::Path(p) = &field.ty {
@ -57,6 +58,14 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
possible_types.push(quote! {
<#p as async_graphql::GQLType>::type_name().to_string()
});
inline_fragment_resolvers.push(quote! {
if name == <#p as async_graphql::GQLType>::type_name() {
if let #ident::#enum_name(obj) = self {
#crate_name::do_resolve(ctx, obj, result).await?;
}
return Ok(());
}
});
} else {
return Err(Error::new_spanned(field, "Invalid type"));
}
@ -228,6 +237,14 @@ pub fn generate(interface_args: &args::Interface, input: &DeriveInput) -> Result
}
.with_position(field.position));
}
async fn resolve_inline_fragment(&self, name: &str, ctx: &#crate_name::ContextSelectionSet<'_>, result: &mut #crate_name::serde_json::Map<String, serde_json::Value>) -> #crate_name::Result<()> {
#(#inline_fragment_resolvers)*
anyhow::bail!(#crate_name::QueryError::UnrecognizedInlineFragment {
object: #gql_typename.to_string(),
name: name.to_string(),
});
}
}
};
Ok(expanded.into())

View File

@ -227,6 +227,13 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
}
.with_position(field.position));
}
async fn resolve_inline_fragment(&self, name: &str, ctx: &#crate_name::ContextSelectionSet<'_>, result: &mut #crate_name::serde_json::Map<String, serde_json::Value>) -> #crate_name::Result<()> {
anyhow::bail!(#crate_name::QueryError::UnrecognizedInlineFragment {
object: #gql_typename.to_string(),
name: name.to_string(),
});
}
}
};
Ok(expanded.into())

View File

@ -28,6 +28,13 @@ pub trait GQLObject: GQLOutputValue {
}
async fn resolve_field(&self, ctx: &Context<'_>, field: &Field) -> Result<serde_json::Value>;
async fn resolve_inline_fragment(
&self,
name: &str,
ctx: &ContextSelectionSet<'_>,
result: &mut serde_json::Map<String, serde_json::Value>,
) -> Result<()>;
}
pub trait GQLInputObject: GQLInputValue {}
@ -78,6 +85,8 @@ macro_rules! impl_scalar {
#[async_trait::async_trait]
impl<T: GQLObject + Send + Sync> GQLOutputValue for T {
async fn resolve(value: &Self, ctx: &ContextSelectionSet<'_>) -> Result<serde_json::Value> {
crate::resolver::do_resolve(ctx, value).await
let mut result = serde_json::Map::<String, serde_json::Value>::new();
crate::resolver::do_resolve(ctx, value, &mut result).await?;
Ok(result.into())
}
}

View File

@ -59,6 +59,12 @@ pub enum QueryError {
#[error("Unknown fragment \"{name}\".")]
UnknownFragment { name: String },
#[error("Object \"{object}\" does not implement interface \"{interface}\"")]
NotImplementedInterface { object: String, interface: String },
#[error("Unrecognized inline fragment \"{name}\" on type \"{object}\"")]
UnrecognizedInlineFragment { object: String, name: String },
}
pub trait ErrorWithPosition {

View File

@ -91,3 +91,5 @@ pub type Error = anyhow::Error;
pub use context::ContextSelectionSet;
#[doc(hidden)]
pub mod registry;
#[doc(hidden)]
pub use resolver::do_resolve;

View File

@ -1,5 +1,5 @@
use crate::{ContextSelectionSet, ErrorWithPosition, GQLObject, QueryError, Result};
use graphql_parser::query::Selection;
use graphql_parser::query::{Selection, TypeCondition};
use std::future::Future;
use std::pin::Pin;
@ -22,11 +22,11 @@ impl<'a, T: GQLObject + Send + Sync> Resolver<'a, T> {
for selection in &self.ctx.item.items {
match selection {
Selection::Field(field) => {
let ctx_field = self.ctx.with_item(field);
if ctx_field.is_skip(&field.directives)? {
if self.ctx.is_skip(&field.directives)? {
continue;
}
let ctx_field = self.ctx.with_item(field);
if field.name.as_str() == "__typename" {
self.result
.insert(ctx_field.result_name(), T::type_name().to_string().into());
@ -39,6 +39,10 @@ impl<'a, T: GQLObject + Send + Sync> Resolver<'a, T> {
);
}
Selection::FragmentSpread(fragment_spread) => {
if self.ctx.is_skip(&fragment_spread.directives)? {
continue;
}
if let Some(fragment) =
self.ctx.fragments.get(&fragment_spread.fragment_name)
{
@ -56,7 +60,23 @@ impl<'a, T: GQLObject + Send + Sync> Resolver<'a, T> {
.into());
}
}
Selection::InlineFragment(_) => {}
Selection::InlineFragment(inline_fragment) => {
if self.ctx.is_skip(&inline_fragment.directives)? {
continue;
}
if let Some(TypeCondition::On(name)) = &inline_fragment.type_condition {
self.obj
.resolve_inline_fragment(
&name,
&self.ctx.with_item(&inline_fragment.selection_set),
self.result,
)
.await?;
} else {
todo!()
}
}
}
}
@ -68,16 +88,14 @@ impl<'a, T: GQLObject + Send + Sync> Resolver<'a, T> {
pub async fn do_resolve<'a, T: GQLObject + Send + Sync>(
ctx: &'a ContextSelectionSet<'a>,
root: &'a T,
) -> Result<serde_json::Value> {
let mut result = serde_json::Map::<String, serde_json::Value>::new();
result: &mut serde_json::Map<String, serde_json::Value>,
) -> Result<()> {
Resolver {
ctx,
obj: root,
result: &mut result,
result,
}
.resolve()
.await?;
Ok(serde_json::Value::Object(result))
Ok(())
}

View File

@ -1,5 +1,6 @@
use crate::{registry, Context, GQLObject, GQLType, QueryError, Result};
use crate::{registry, Context, ContextSelectionSet, GQLObject, GQLType, QueryError, Result};
use graphql_parser::query::Field;
use serde_json::{Map, Value};
use std::borrow::Cow;
pub struct GQLEmptyMutation;
@ -27,4 +28,13 @@ impl GQLObject for GQLEmptyMutation {
async fn resolve_field(&self, _ctx: &Context<'_>, _name: &Field) -> Result<serde_json::Value> {
return Err(QueryError::NotConfiguredMutations.into());
}
async fn resolve_inline_fragment(
&self,
_name: &str,
_ctx: &ContextSelectionSet<'_>,
_result: &mut Map<String, Value>,
) -> Result<()> {
return Err(QueryError::NotConfiguredMutations.into());
}
}

View File

@ -1,6 +1,7 @@
use crate::model::{__Schema, __Type};
use crate::{
registry, Context, ErrorWithPosition, GQLObject, GQLOutputValue, GQLType, Result, Value,
registry, Context, ContextSelectionSet, ErrorWithPosition, GQLObject, GQLOutputValue, GQLType,
QueryError, Result, Value,
};
use graphql_parser::query::Field;
use std::borrow::Cow;
@ -52,4 +53,16 @@ impl<T: GQLObject + Send + Sync> GQLObject for QueryRoot<T> {
return self.inner.resolve_field(ctx, field).await;
}
async fn resolve_inline_fragment(
&self,
name: &str,
_ctx: &ContextSelectionSet<'_>,
_result: &mut serde_json::Map<String, serde_json::Value>,
) -> Result<()> {
anyhow::bail!(QueryError::UnrecognizedInlineFragment {
object: T::type_name().to_string(),
name: name.to_string(),
});
}
}