Add serial attribute for MergedObject macro. #539

This commit is contained in:
Sunli 2021-06-16 15:08:33 +08:00
parent 0ed444654d
commit 5691f6ca71
6 changed files with 92 additions and 63 deletions

View File

@ -487,6 +487,8 @@ pub struct MergedObject {
pub extends: bool,
#[darling(default)]
pub visible: Option<Visible>,
#[darling(default)]
pub serial: bool,
}
#[derive(FromField)]

View File

@ -55,6 +55,12 @@ pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream
};
let visible = visible_fn(&object_args.visible);
let resolve_container = if object_args.serial {
quote! { #crate_name::resolver_utils::resolve_container_serial(ctx, self).await }
} else {
quote! { #crate_name::resolver_utils::resolve_container(ctx, self).await }
};
let expanded = quote! {
#[allow(clippy::all, clippy::pedantic)]
impl #impl_generics #crate_name::Type for #ident #ty_generics #where_clause {
@ -105,7 +111,7 @@ pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream
#[#crate_name::async_trait::async_trait]
impl #impl_generics #crate_name::OutputType for #ident #ty_generics #where_clause {
async fn resolve(&self, ctx: &#crate_name::ContextSelectionSet<'_>, _field: &#crate_name::Positioned<#crate_name::parser::types::Field>) -> #crate_name::ServerResult<#crate_name::Value> {
#crate_name::resolver_utils::resolve_container(ctx, self).await
#resolve_container
}
}

View File

@ -1104,6 +1104,7 @@ pub use async_graphql_derive::NewType;
/// | extends | Add fields to an entity that's defined in another service | bool | Y |
/// | visible | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y |
/// | visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
/// | serial | Resolve each field sequentially. | bool | Y |
///
/// # Examples
///

View File

@ -4,9 +4,8 @@ use indexmap::IndexMap;
use crate::parser::types::Field;
use crate::registry::{MetaType, Registry};
use crate::resolver_utils::resolve_container;
use crate::{
CacheControl, ContainerType, Context, ContextSelectionSet, ObjectType, OutputType, Positioned,
CacheControl, ContainerType, Context, ContextSelectionSet, OutputType, Positioned,
ServerResult, SimpleObject, Type, Value,
};
@ -59,8 +58,8 @@ impl<A: Type, B: Type> Type for MergedObject<A, B> {
#[async_trait::async_trait]
impl<A, B> ContainerType for MergedObject<A, B>
where
A: ObjectType,
B: ObjectType,
A: ContainerType,
B: ContainerType,
{
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
match self.0.resolve_field(ctx).await {
@ -82,25 +81,18 @@ where
#[async_trait::async_trait]
impl<A, B> OutputType for MergedObject<A, B>
where
A: ObjectType,
B: ObjectType,
A: ContainerType,
B: ContainerType,
{
async fn resolve(
&self,
ctx: &ContextSelectionSet<'_>,
_ctx: &ContextSelectionSet<'_>,
_field: &Positioned<Field>,
) -> ServerResult<Value> {
resolve_container(ctx, self).await
unreachable!()
}
}
impl<A, B> ObjectType for MergedObject<A, B>
where
A: ObjectType,
B: ObjectType,
{
}
#[doc(hidden)]
#[derive(SimpleObject, Default)]
#[graphql(internal, dummy)]

View File

@ -16,45 +16,6 @@ struct Object3 {
c: i32,
}
#[tokio::test]
pub async fn test_merged_object() {
type MyObj =
MergedObject<Object1, MergedObject<Object2, MergedObject<Object3, MergedObjectTail>>>;
struct Query;
#[Object]
impl Query {
async fn obj(&self) -> MyObj {
MergedObject(
Object1 { a: 10 },
MergedObject(
Object2 { b: 20 },
MergedObject(Object3 { c: 30 }, MergedObjectTail),
),
)
}
}
assert_eq!(
MyObj::type_name(),
"Object1_Object2_Object3_MergedObjectTail"
);
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let query = "{ obj { a b c } }";
assert_eq!(
schema.execute(query).await.into_result().unwrap().data,
value!({
"obj": {
"a": 10,
"b": 20,
"c": 30,
}
})
);
}
#[tokio::test]
pub async fn test_merged_object_macro() {
#[derive(MergedObject)]

View File

@ -4,7 +4,7 @@ use std::time::Duration;
use tokio::sync::Mutex;
#[tokio::test]
pub async fn test_mutation_execution_order() {
pub async fn test_root_mutation_execution_order() {
type List = Arc<Mutex<Vec<i32>>>;
struct QueryRoot;
@ -38,8 +38,7 @@ pub async fn test_mutation_execution_order() {
.data(list.clone())
.finish();
schema.execute("mutation { append1 append2 }").await;
assert_eq!(list.lock().await[0], 1);
assert_eq!(list.lock().await[1], 2);
assert_eq!(&*list.lock().await, &[1, 2]);
}
#[tokio::test]
@ -86,7 +85,7 @@ pub async fn test_mutation_fragment() {
}
#[tokio::test]
pub async fn test_serial() {
pub async fn test_serial_object() {
type List = Arc<Mutex<Vec<i32>>>;
struct MyObj;
@ -129,8 +128,7 @@ pub async fn test_serial() {
.data(list.clone())
.finish();
schema.execute("mutation { obj { append1 append2 } }").await;
assert_eq!(list.lock().await[0], 1);
assert_eq!(list.lock().await[1], 2);
assert_eq!(&*list.lock().await, &[1, 2]);
}
#[tokio::test]
@ -181,6 +179,75 @@ pub async fn test_serial_simple_object() {
.data(list.clone())
.finish();
schema.execute("mutation { obj { append1 append2 } }").await;
assert_eq!(list.lock().await[0], 1);
assert_eq!(list.lock().await[1], 2);
assert_eq!(&*list.lock().await, &[1, 2]);
}
#[tokio::test]
pub async fn test_serial_merged_object() {
type List = Arc<Mutex<Vec<i32>>>;
#[derive(MergedObject)]
#[graphql(serial)]
struct MyObj(MyObj1, MyObj2);
struct MyObj1;
#[Object]
impl MyObj1 {
async fn append1(&self, ctx: &Context<'_>) -> bool {
tokio::time::sleep(Duration::from_secs(1)).await;
ctx.data_unchecked::<List>().lock().await.push(1);
true
}
async fn append2(&self, ctx: &Context<'_>) -> bool {
tokio::time::sleep(Duration::from_millis(500)).await;
ctx.data_unchecked::<List>().lock().await.push(2);
true
}
}
struct MyObj2;
#[Object]
impl MyObj2 {
async fn append3(&self, ctx: &Context<'_>) -> bool {
tokio::time::sleep(Duration::from_millis(200)).await;
ctx.data_unchecked::<List>().lock().await.push(3);
true
}
async fn append4(&self, ctx: &Context<'_>) -> bool {
tokio::time::sleep(Duration::from_millis(700)).await;
ctx.data_unchecked::<List>().lock().await.push(4);
true
}
}
struct QueryRoot;
#[Object]
impl QueryRoot {
async fn value(&self) -> i32 {
10
}
}
struct MutationRoot;
#[Object]
impl MutationRoot {
async fn obj(&self) -> MyObj {
MyObj(MyObj1, MyObj2)
}
}
let list = List::default();
let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
.data(list.clone())
.finish();
schema
.execute("mutation { obj { append1 append2 append3 append4 } }")
.await;
assert_eq!(&*list.lock().await, &[1, 2, 3, 4]);
}