Add serial attribute for SimpleObject and Object macros. #539

This commit is contained in:
Sunli 2021-06-15 08:53:26 +08:00
parent 6d28bffcce
commit b7b9abf40d
5 changed files with 120 additions and 3 deletions

View File

@ -173,6 +173,8 @@ pub struct SimpleObject {
pub visible: Option<Visible>,
#[darling(default, multiple, rename = "concrete")]
pub concretes: Vec<ConcreteType>,
#[darling(default)]
pub serial: bool,
}
#[derive(FromMeta, Default)]
@ -199,6 +201,7 @@ pub struct Object {
pub extends: bool,
pub use_type_description: bool,
pub visible: Option<Visible>,
pub serial: bool,
}
pub enum ComplexityType {

View File

@ -578,6 +578,11 @@ pub fn generate(
}
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! {
#item_impl
@ -645,7 +650,7 @@ pub fn generate(
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

@ -170,6 +170,12 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
};
}
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 = if object_args.concretes.is_empty() {
quote! {
#[allow(clippy::all, clippy::pedantic)]
@ -216,7 +222,7 @@ pub fn generate(object_args: &args::SimpleObject) -> 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
}
}
@ -281,7 +287,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
#[#crate_name::async_trait::async_trait]
impl #crate_name::OutputType for #concrete_type {
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

@ -284,6 +284,7 @@ pub type FieldResult<T> = Result<T>;
/// | 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 |
/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y |
/// | serial | Resolve each field sequentially. | bool | Y |
/// | key | Is entity key(for Federation) | bool | Y |
///
/// # Valid field return types
@ -430,6 +431,8 @@ pub use async_graphql_derive::Object;
/// | 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 |
/// | concretes | Specify how the concrete type of the generic SimpleObject should be implemented. *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html#generic-simpleobjects) | ConcreteType | Y |
/// | serial | Resolve each field sequentially. | bool | Y |
///
/// # Field parameters
///

View File

@ -84,3 +84,103 @@ pub async fn test_mutation_fragment() {
})
);
}
#[tokio::test]
pub async fn test_serial() {
type List = Arc<Mutex<Vec<i32>>>;
struct MyObj;
#[Object(serial)]
impl MyObj {
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 QueryRoot;
#[Object]
impl QueryRoot {
async fn value(&self) -> i32 {
10
}
}
struct MutationRoot;
#[Object]
impl MutationRoot {
async fn obj(&self) -> MyObj {
MyObj
}
}
let list = List::default();
let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
.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);
}
#[tokio::test]
pub async fn test_serial_simple_object() {
type List = Arc<Mutex<Vec<i32>>>;
#[derive(SimpleObject)]
#[graphql(complex, serial)]
struct MyObj {
value: i32,
}
#[ComplexObject]
impl MyObj {
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 QueryRoot;
#[Object]
impl QueryRoot {
async fn value(&self) -> i32 {
10
}
}
struct MutationRoot;
#[Object]
impl MutationRoot {
async fn obj(&self) -> MyObj {
MyObj { value: 10 }
}
}
let list = List::default();
let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
.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);
}