Add support flatten
attribute for SimpleObject
, ComplexObject
and Object
macros. #533
This commit is contained in:
parent
c6bb4c026c
commit
3217f7aecd
|
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [3.0.22] 2022-1-11
|
||||||
|
|
||||||
|
- Add support `flatten` attribute for `SimpleObject`, `ComplexObject` and `Object` macros. [#533](https://github.com/async-graphql/async-graphql/issues/533)
|
||||||
|
|
||||||
## [3.0.21] 2022-1-11
|
## [3.0.21] 2022-1-11
|
||||||
|
|
||||||
- Add `Union` and `Interface` support for trait objects. [#780](https://github.com/async-graphql/async-graphql/issues/780)
|
- Add `Union` and `Interface` support for trait objects. [#780](https://github.com/async-graphql/async-graphql/issues/780)
|
||||||
|
|
|
@ -265,6 +265,7 @@ pub struct ObjectField {
|
||||||
pub complexity: Option<ComplexityType>,
|
pub complexity: Option<ComplexityType>,
|
||||||
#[darling(default, multiple)]
|
#[darling(default, multiple)]
|
||||||
pub derived: Vec<DerivedField>,
|
pub derived: Vec<DerivedField>,
|
||||||
|
pub flatten: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromMeta, Default, Clone)]
|
#[derive(FromMeta, Default, Clone)]
|
||||||
|
@ -698,6 +699,9 @@ pub struct ComplexObjectField {
|
||||||
pub guard: Option<SpannedValue<String>>,
|
pub guard: Option<SpannedValue<String>>,
|
||||||
pub visible: Option<Visible>,
|
pub visible: Option<Visible>,
|
||||||
pub complexity: Option<ComplexityType>,
|
pub complexity: Option<ComplexityType>,
|
||||||
|
#[darling(multiple)]
|
||||||
|
pub derived: Vec<DerivedField>,
|
||||||
|
pub flatten: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromMeta, Default)]
|
#[derive(FromMeta, Default)]
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub fn generate(
|
||||||
let mut derived_impls = vec![];
|
let mut derived_impls = vec![];
|
||||||
for item in &mut item_impl.items {
|
for item in &mut item_impl.items {
|
||||||
if let ImplItem::Method(method) = item {
|
if let ImplItem::Method(method) = item {
|
||||||
let method_args: args::ObjectField =
|
let method_args: args::ComplexObjectField =
|
||||||
parse_graphql_attrs(&method.attrs)?.unwrap_or_default();
|
parse_graphql_attrs(&method.attrs)?.unwrap_or_default();
|
||||||
|
|
||||||
for derived in method_args.derived {
|
for derived in method_args.derived {
|
||||||
|
@ -116,12 +116,47 @@ pub fn generate(
|
||||||
|
|
||||||
for item in &mut item_impl.items {
|
for item in &mut item_impl.items {
|
||||||
if let ImplItem::Method(method) = item {
|
if let ImplItem::Method(method) = item {
|
||||||
let method_args: args::ObjectField =
|
let method_args: args::ComplexObjectField =
|
||||||
parse_graphql_attrs(&method.attrs)?.unwrap_or_default();
|
parse_graphql_attrs(&method.attrs)?.unwrap_or_default();
|
||||||
if method_args.skip {
|
if method_args.skip {
|
||||||
remove_graphql_attrs(&mut method.attrs);
|
remove_graphql_attrs(&mut method.attrs);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let cfg_attrs = get_cfg_attrs(&method.attrs);
|
||||||
|
|
||||||
|
if method_args.flatten {
|
||||||
|
let ty = match &method.sig.output {
|
||||||
|
ReturnType::Type(_, ty) => OutputType::parse(ty)?,
|
||||||
|
ReturnType::Default => {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&method.sig.output,
|
||||||
|
"Flatten resolver must have a return type",
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let ty = ty.value_type();
|
||||||
|
let ident = &method.sig.ident;
|
||||||
|
|
||||||
|
schema_fields.push(quote! {
|
||||||
|
#crate_name::static_assertions::assert_impl_one!(#ty: #crate_name::ObjectType);
|
||||||
|
<#ty>::create_type_info(registry);
|
||||||
|
if let #crate_name::registry::MetaType::Object { fields: obj_fields, .. } =
|
||||||
|
registry.create_fake_output_type::<#ty>() {
|
||||||
|
fields.extend(obj_fields);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resolvers.push(quote! {
|
||||||
|
#(#cfg_attrs)*
|
||||||
|
if let ::std::option::Option::Some(value) = #crate_name::ContainerType::resolve_field(&self.#ident().await, ctx).await? {
|
||||||
|
return ::std::result::Result::Ok(std::option::Option::Some(value));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
remove_graphql_attrs(&mut method.attrs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let field_name = method_args.name.clone().unwrap_or_else(|| {
|
let field_name = method_args.name.clone().unwrap_or_else(|| {
|
||||||
object_args
|
object_args
|
||||||
|
@ -151,7 +186,6 @@ pub fn generate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cfg_attrs = get_cfg_attrs(&method.attrs);
|
|
||||||
|
|
||||||
let args = extract_input_args(&crate_name, method)?;
|
let args = extract_input_args(&crate_name, method)?;
|
||||||
let ty = match &method.sig.output {
|
let ty = match &method.sig.output {
|
||||||
|
|
|
@ -258,6 +258,41 @@ pub fn generate(
|
||||||
if method.sig.asyncness.is_none() {
|
if method.sig.asyncness.is_none() {
|
||||||
return Err(Error::new_spanned(&method, "Must be asynchronous").into());
|
return Err(Error::new_spanned(&method, "Must be asynchronous").into());
|
||||||
}
|
}
|
||||||
|
let cfg_attrs = get_cfg_attrs(&method.attrs);
|
||||||
|
|
||||||
|
if method_args.flatten {
|
||||||
|
let ty = match &method.sig.output {
|
||||||
|
ReturnType::Type(_, ty) => OutputType::parse(ty)?,
|
||||||
|
ReturnType::Default => {
|
||||||
|
return Err(Error::new_spanned(
|
||||||
|
&method.sig.output,
|
||||||
|
"Flatten resolver must have a return type",
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let ty = ty.value_type();
|
||||||
|
let ident = &method.sig.ident;
|
||||||
|
|
||||||
|
schema_fields.push(quote! {
|
||||||
|
#crate_name::static_assertions::assert_impl_one!(#ty: #crate_name::ObjectType);
|
||||||
|
<#ty>::create_type_info(registry);
|
||||||
|
if let #crate_name::registry::MetaType::Object { fields: obj_fields, .. } =
|
||||||
|
registry.create_fake_output_type::<#ty>() {
|
||||||
|
fields.extend(obj_fields);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resolvers.push(quote! {
|
||||||
|
#(#cfg_attrs)*
|
||||||
|
if let ::std::option::Option::Some(value) = #crate_name::ContainerType::resolve_field(&self.#ident().await, ctx).await? {
|
||||||
|
return ::std::result::Result::Ok(std::option::Option::Some(value));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
remove_graphql_attrs(&mut method.attrs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let field_name = method_args.name.clone().unwrap_or_else(|| {
|
let field_name = method_args.name.clone().unwrap_or_else(|| {
|
||||||
object_args
|
object_args
|
||||||
|
@ -287,7 +322,6 @@ pub fn generate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let cfg_attrs = get_cfg_attrs(&method.attrs);
|
|
||||||
|
|
||||||
let args = extract_input_args(&crate_name, method)?;
|
let args = extract_input_args(&crate_name, method)?;
|
||||||
let mut schema_args = Vec::new();
|
let mut schema_args = Vec::new();
|
||||||
|
|
|
@ -153,6 +153,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
||||||
|
|
||||||
let visible = visible_fn(&field.visible);
|
let visible = visible_fn(&field.visible);
|
||||||
|
|
||||||
|
if !field.flatten {
|
||||||
schema_fields.push(quote! {
|
schema_fields.push(quote! {
|
||||||
fields.insert(::std::borrow::ToOwned::to_owned(#field_name), #crate_name::registry::MetaField {
|
fields.insert(::std::borrow::ToOwned::to_owned(#field_name), #crate_name::registry::MetaField {
|
||||||
name: ::std::borrow::ToOwned::to_owned(#field_name),
|
name: ::std::borrow::ToOwned::to_owned(#field_name),
|
||||||
|
@ -168,6 +169,16 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
||||||
compute_complexity: ::std::option::Option::None,
|
compute_complexity: ::std::option::Option::None,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
schema_fields.push(quote! {
|
||||||
|
#crate_name::static_assertions::assert_impl_one!(#ty: #crate_name::ObjectType);
|
||||||
|
#ty::create_type_info(registry);
|
||||||
|
if let #crate_name::registry::MetaType::Object { fields: obj_fields, .. } =
|
||||||
|
registry.create_fake_output_type::<#ty>() {
|
||||||
|
fields.extend(obj_fields);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let guard_map_err = quote! {
|
let guard_map_err = quote! {
|
||||||
.map_err(|err| err.into_server_error(ctx.item.pos))
|
.map_err(|err| err.into_server_error(ctx.item.pos))
|
||||||
|
@ -203,15 +214,14 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
||||||
false => quote! { #ty },
|
false => quote! { #ty },
|
||||||
};
|
};
|
||||||
|
|
||||||
getters.push(
|
if !field.flatten {
|
||||||
quote! {
|
getters.push(quote! {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#vis async fn #ident(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::Result<#ty> {
|
#vis async fn #ident(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::Result<#ty> {
|
||||||
::std::result::Result::Ok(#block)
|
::std::result::Result::Ok(#block)
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
resolvers.push(quote! {
|
resolvers.push(quote! {
|
||||||
if ctx.item.node.name.node == #field_name {
|
if ctx.item.node.name.node == #field_name {
|
||||||
|
@ -224,6 +234,13 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
||||||
return #crate_name::OutputType::resolve(&obj, &ctx_obj, ctx.item).await.map(::std::option::Option::Some);
|
return #crate_name::OutputType::resolve(&obj, &ctx_obj, ctx.item).await.map(::std::option::Option::Some);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
resolvers.push(quote! {
|
||||||
|
if let ::std::option::Option::Some(value) = #crate_name::ContainerType::resolve_field(&self.#ident, ctx).await? {
|
||||||
|
return ::std::result::Result::Ok(std::option::Option::Some(value));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !object_args.fake && resolvers.is_empty() {
|
if !object_args.fake && resolvers.is_empty() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ some simple fields, and use the `ComplexObject` macro to define some other field
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | Y |
|
||||||
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
|
@ -19,7 +19,7 @@ some simple fields, and use the `ComplexObject` macro to define some other field
|
||||||
# Field attributes
|
# Field attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|----------|
|
||||||
| skip | Skip this field | bool | Y |
|
| skip | Skip this field | bool | Y |
|
||||||
| name | Field name | string | Y |
|
| name | Field name | string | Y |
|
||||||
| desc | Field description | string | Y |
|
| desc | Field description | string | Y |
|
||||||
|
@ -35,11 +35,12 @@ some simple fields, and use the `ComplexObject` macro to define some other field
|
||||||
| complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y |
|
| complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y |
|
||||||
| complexity | Custom field complexity. | string | Y |
|
| complexity | Custom field complexity. | string | Y |
|
||||||
| derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y |
|
| derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y |
|
||||||
|
| flatten | Similar to serde (flatten) | boolean | Y |
|
||||||
|
|
||||||
# Field argument attributes
|
# Field argument attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|------------------------------------------|------------ |----------|
|
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------|-------------|----------|
|
||||||
| name | Argument name | string | Y |
|
| name | Argument name | string | Y |
|
||||||
| desc | Argument description | string | Y |
|
| desc | Argument description | string | Y |
|
||||||
| default | Use `Default::default` for default value | none | Y |
|
| default | Use `Default::default` for default value | none | Y |
|
||||||
|
|
|
@ -5,7 +5,7 @@ Define a directive for query.
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | 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 | 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 |
|
| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
||||||
|
@ -16,7 +16,7 @@ Define a directive for query.
|
||||||
# Directive attributes
|
# Directive attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|------------------------------------------|------------ |----------|
|
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------|-------------|----------|
|
||||||
| name | Argument name | string | Y |
|
| name | Argument name | string | Y |
|
||||||
| desc | Argument description | string | Y |
|
| desc | Argument description | string | Y |
|
||||||
| default | Use `Default::default` for default value | none | Y |
|
| default | Use `Default::default` for default value | none | Y |
|
||||||
|
|
|
@ -5,7 +5,7 @@ Define a GraphQL enum
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|---------------------------|----------|----------|
|
|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Enum name | string | Y |
|
| name | Enum name | string | Y |
|
||||||
| rename_items | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_items | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
| remote | Derive a remote enum | string | Y |
|
| remote | Derive a remote enum | string | Y |
|
||||||
|
@ -15,7 +15,7 @@ Define a GraphQL enum
|
||||||
# Item attributes
|
# Item attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|-------------|---------------------------|----------|----------|
|
|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Item name | string | Y |
|
| name | Item name | string | Y |
|
||||||
| deprecation | Item deprecated | bool | Y |
|
| deprecation | Item deprecated | bool | Y |
|
||||||
| deprecation | Item deprecation reason | string | Y |
|
| deprecation | Item deprecation reason | string | Y |
|
||||||
|
|
|
@ -5,7 +5,7 @@ Define a GraphQL input object
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | Y |
|
||||||
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | 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 | 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 |
|
||||||
|
@ -14,7 +14,7 @@ Define a GraphQL input object
|
||||||
# Field attributes
|
# Field attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|------------------------------------------|-------------|----------|
|
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------|-------------|----------|
|
||||||
| name | Field name | string | Y |
|
| name | Field name | string | Y |
|
||||||
| default | Use `Default::default` for default value | none | Y |
|
| default | Use `Default::default` for default value | none | Y |
|
||||||
| default | Argument default value | literal | Y |
|
| default | Argument default value | literal | Y |
|
||||||
|
|
|
@ -5,7 +5,7 @@ Define a GraphQL interface
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | Y |
|
||||||
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
|
@ -17,7 +17,7 @@ Define a GraphQL interface
|
||||||
# Field attributes
|
# Field attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|-------------|---------------------------|----------|----------|
|
|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------|----------|
|
||||||
| name | Field name | string | N |
|
| name | Field name | string | N |
|
||||||
| type | Field type | string | N |
|
| type | Field type | string | N |
|
||||||
| method | Rust resolver method name. If specified, `name` will not be camelCased in schema definition | string | Y |
|
| method | Rust resolver method name. If specified, `name` will not be camelCased in schema definition | string | Y |
|
||||||
|
@ -34,7 +34,7 @@ Define a GraphQL interface
|
||||||
# Field argument attributes
|
# Field argument attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|------------------------------------------|-------------|----------|
|
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------|-------------|----------|
|
||||||
| name | Argument name | string | N |
|
| name | Argument name | string | N |
|
||||||
| type | Argument type | string | N |
|
| type | Argument type | string | N |
|
||||||
| desc | Argument description | string | Y |
|
| desc | Argument description | string | Y |
|
||||||
|
|
|
@ -5,7 +5,7 @@ Define a merged object with multiple object types.
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | Y |
|
||||||
| cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
| cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
||||||
| extends | Add fields to an entity that's defined in another service | bool | Y |
|
| extends | Add fields to an entity that's defined in another service | bool | Y |
|
||||||
|
|
|
@ -5,7 +5,7 @@ Define a merged subscription with multiple subscription types.
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | Y |
|
||||||
| extends | Add fields to an entity that's defined in another service | bool | Y |
|
| 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 | 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 |
|
||||||
|
|
|
@ -5,7 +5,7 @@ It also implements `From<InnerType>` and `Into<InnerType>`.
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|-------------|---------------------------|----------|----------|
|
|----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | string | Y |
|
| name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | string | Y |
|
||||||
| name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | bool | Y |
|
| name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | bool | Y |
|
||||||
| visible(Only valid for new scalars) | 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(Only valid for new scalars) | 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 |
|
||||||
|
|
|
@ -7,7 +7,7 @@ All methods are converted to camelCase.
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | Y |
|
||||||
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
|
@ -21,7 +21,7 @@ All methods are converted to camelCase.
|
||||||
# Field attributes
|
# Field attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|----------|
|
||||||
| skip | Skip this field | bool | Y |
|
| skip | Skip this field | bool | Y |
|
||||||
| name | Field name | string | Y |
|
| name | Field name | string | Y |
|
||||||
| desc | Field description | string | Y |
|
| desc | Field description | string | Y |
|
||||||
|
@ -37,11 +37,12 @@ All methods are converted to camelCase.
|
||||||
| complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y |
|
| complexity | Custom field complexity. *[See also the Book](https://async-graphql.github.io/async-graphql/en/depth_and_complexity.html).* | bool | Y |
|
||||||
| complexity | Custom field complexity. | string | Y |
|
| complexity | Custom field complexity. | string | Y |
|
||||||
| derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y |
|
| derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y |
|
||||||
|
| flatten | Similar to serde (flatten) | boolean | Y |
|
||||||
|
|
||||||
# Field argument attributes
|
# Field argument attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|------------------------------------------|------------ |----------|
|
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------|-------------|----------|
|
||||||
| name | Argument name | string | Y |
|
| name | Argument name | string | Y |
|
||||||
| desc | Argument description | string | Y |
|
| desc | Argument description | string | Y |
|
||||||
| default | Use `Default::default` for default value | none | Y |
|
| default | Use `Default::default` for default value | none | Y |
|
||||||
|
@ -56,7 +57,7 @@ All methods are converted to camelCase.
|
||||||
# Derived argument attributes
|
# Derived argument attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|------------------------------------------|------------ |----------|
|
|-----------|------------------------------------------------|--------|----------|
|
||||||
| name | Generated derived field name | string | N |
|
| name | Generated derived field name | string | N |
|
||||||
| into | Type to derived an into | string | Y |
|
| into | Type to derived an into | string | Y |
|
||||||
| with | Function to apply to manage advanced use cases | string | Y |
|
| with | Function to apply to manage advanced use cases | string | Y |
|
||||||
|
|
|
@ -3,6 +3,6 @@ Define a Scalar
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|-------------|---------------------------|----------|----------|
|
|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Scalar name | string | Y |
|
| name | Scalar name | string | Y |
|
||||||
| specified_by_url | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y |
|
| specified_by_url | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y |
|
||||||
|
|
|
@ -7,7 +7,7 @@ Similar to `Object`, but defined on a structure that automatically generates get
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | Y |
|
||||||
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
| cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
| cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
||||||
|
@ -20,7 +20,7 @@ Similar to `Object`, but defined on a structure that automatically generates get
|
||||||
# Field attributes
|
# Field attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------|----------|
|
||||||
| skip | Skip this field | bool | Y |
|
| skip | Skip this field | bool | Y |
|
||||||
| name | Field name | string | Y |
|
| name | Field name | string | Y |
|
||||||
| deprecation | Field deprecated | bool | Y |
|
| deprecation | Field deprecated | bool | Y |
|
||||||
|
@ -34,11 +34,12 @@ Similar to `Object`, but defined on a structure that automatically generates get
|
||||||
| guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y |
|
| guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | 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 | 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 |
|
| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
||||||
|
| flatten | Similar to serde (flatten) | boolean | Y |
|
||||||
|
|
||||||
# Derived attributes
|
# Derived attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|------------------------------------------|------------ |----------|
|
|-----------|------------------------------------------------|--------|----------|
|
||||||
| name | Generated derived field name | string | N |
|
| name | Generated derived field name | string | N |
|
||||||
| into | Type to derived an into | string | Y |
|
| into | Type to derived an into | string | Y |
|
||||||
| owned | Field resolver return a ownedship value | bool | Y |
|
| owned | Field resolver return a ownedship value | bool | Y |
|
||||||
|
|
|
@ -10,7 +10,7 @@ The filter function should be synchronous.
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|---------------|---------------------------|----------|----------|
|
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | Y |
|
||||||
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
| rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y |
|
||||||
|
@ -22,7 +22,7 @@ The filter function should be synchronous.
|
||||||
# Field attributes
|
# Field attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|-------------|---------------------------|----------|----------|
|
|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Field name | string | Y |
|
| name | Field name | string | Y |
|
||||||
| deprecation | Field deprecated | bool | Y |
|
| deprecation | Field deprecated | bool | Y |
|
||||||
| deprecation | Field deprecation reason | string | Y |
|
| deprecation | Field deprecation reason | string | Y |
|
||||||
|
@ -36,7 +36,7 @@ The filter function should be synchronous.
|
||||||
# Field argument attributes
|
# Field argument attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|--------------|------------------------------------------|------------ |----------|
|
|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------|-------------|----------|
|
||||||
| name | Argument name | string | Y |
|
| name | Argument name | string | Y |
|
||||||
| desc | Argument description | string | Y |
|
| desc | Argument description | string | Y |
|
||||||
| default | Use `Default::default` for default value | none | Y |
|
| default | Use `Default::default` for default value | none | Y |
|
||||||
|
|
|
@ -5,7 +5,7 @@ Define a GraphQL union
|
||||||
# Macro attributes
|
# Macro attributes
|
||||||
|
|
||||||
| Attribute | description | Type | Optional |
|
| Attribute | description | Type | Optional |
|
||||||
|-------------|---------------------------|----------|----------|
|
|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------|
|
||||||
| name | Object name | string | Y |
|
| name | Object name | string | 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 | 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 |
|
| visible | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
||||||
|
|
|
@ -8,7 +8,8 @@ use indexmap::IndexMap;
|
||||||
use crate::extensions::ResolveInfo;
|
use crate::extensions::ResolveInfo;
|
||||||
use crate::parser::types::Selection;
|
use crate::parser::types::Selection;
|
||||||
use crate::{
|
use crate::{
|
||||||
Context, ContextBase, ContextSelectionSet, Name, OutputType, ServerError, ServerResult, Value,
|
Context, ContextBase, ContextSelectionSet, Error, Name, OutputType, ServerError, ServerResult,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents a GraphQL container object.
|
/// Represents a GraphQL container object.
|
||||||
|
@ -84,6 +85,23 @@ impl<T: ContainerType + ?Sized> ContainerType for Box<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<T: ContainerType, E: Into<Error> + Send + Sync + Clone> ContainerType for Result<T, E> {
|
||||||
|
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
|
||||||
|
match self {
|
||||||
|
Ok(value) => T::resolve_field(value, ctx).await,
|
||||||
|
Err(err) => Err(ctx.set_error_path(err.clone().into().into_server_error(ctx.item.pos))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn find_entity(&self, ctx: &Context<'_>, params: &Value) -> ServerResult<Option<Value>> {
|
||||||
|
match self {
|
||||||
|
Ok(value) => T::find_entity(value, ctx, params).await,
|
||||||
|
Err(err) => Err(ctx.set_error_path(err.clone().into().into_server_error(ctx.item.pos))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve an container by executing each of the fields concurrently.
|
/// Resolve an container by executing each of the fields concurrently.
|
||||||
pub async fn resolve_container<'a, T: ContainerType + ?Sized>(
|
pub async fn resolve_container<'a, T: ContainerType + ?Sized>(
|
||||||
ctx: &ContextSelectionSet<'a>,
|
ctx: &ContextSelectionSet<'a>,
|
||||||
|
|
|
@ -300,3 +300,131 @@ pub async fn test_complex_object_with_generic_concrete_type() {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_flatten() {
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
struct A {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
#[graphql(complex)]
|
||||||
|
struct B {
|
||||||
|
#[graphql(skip)]
|
||||||
|
a: A,
|
||||||
|
c: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ComplexObject]
|
||||||
|
impl B {
|
||||||
|
#[graphql(flatten)]
|
||||||
|
async fn a(&self) -> &A {
|
||||||
|
&self.a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
async fn obj(&self) -> B {
|
||||||
|
B {
|
||||||
|
a: A { a: 100, b: 200 },
|
||||||
|
c: 300,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
|
let query = "{ __type(name: \"B\") { fields { name } } }";
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.data,
|
||||||
|
value!({
|
||||||
|
"__type": {
|
||||||
|
"fields": [
|
||||||
|
{"name": "c"},
|
||||||
|
{"name": "a"},
|
||||||
|
{"name": "b"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let query = "{ obj { a b c } }";
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.data,
|
||||||
|
value!({
|
||||||
|
"obj": {
|
||||||
|
"a": 100,
|
||||||
|
"b": 200,
|
||||||
|
"c": 300,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_flatten_with_result() {
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
struct A {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
#[graphql(complex)]
|
||||||
|
struct B {
|
||||||
|
#[graphql(skip)]
|
||||||
|
a: A,
|
||||||
|
c: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ComplexObject]
|
||||||
|
impl B {
|
||||||
|
#[graphql(flatten)]
|
||||||
|
async fn a(&self) -> FieldResult<&A> {
|
||||||
|
Ok(&self.a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
async fn obj(&self) -> B {
|
||||||
|
B {
|
||||||
|
a: A { a: 100, b: 200 },
|
||||||
|
c: 300,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
|
let query = "{ __type(name: \"B\") { fields { name } } }";
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.data,
|
||||||
|
value!({
|
||||||
|
"__type": {
|
||||||
|
"fields": [
|
||||||
|
{"name": "c"},
|
||||||
|
{"name": "a"},
|
||||||
|
{"name": "b"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let query = "{ obj { a b c } }";
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.data,
|
||||||
|
value!({
|
||||||
|
"obj": {
|
||||||
|
"a": 100,
|
||||||
|
"b": 200,
|
||||||
|
"c": 300,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
60
tests/object.rs
Normal file
60
tests/object.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_flatten() {
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
struct A {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl B {
|
||||||
|
#[graphql(flatten)]
|
||||||
|
async fn a(&self) -> A {
|
||||||
|
A { a: 100, b: 200 }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn c(&self) -> i32 {
|
||||||
|
300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
async fn obj(&self) -> B {
|
||||||
|
B
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
|
let query = "{ __type(name: \"B\") { fields { name } } }";
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.data,
|
||||||
|
value!({
|
||||||
|
"__type": {
|
||||||
|
"fields": [
|
||||||
|
{"name": "a"},
|
||||||
|
{"name": "b"},
|
||||||
|
{"name": "c"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let query = "{ obj { a b c } }";
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.data,
|
||||||
|
value!({
|
||||||
|
"obj": {
|
||||||
|
"a": 100,
|
||||||
|
"b": 200,
|
||||||
|
"c": 300,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
56
tests/simple_object.rs
Normal file
56
tests/simple_object.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use async_graphql::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_flatten() {
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
struct A {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SimpleObject)]
|
||||||
|
struct B {
|
||||||
|
#[graphql(flatten)]
|
||||||
|
a: A,
|
||||||
|
c: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
async fn obj(&self) -> B {
|
||||||
|
B {
|
||||||
|
a: A { a: 100, b: 200 },
|
||||||
|
c: 300,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
|
let query = "{ __type(name: \"B\") { fields { name } } }";
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.data,
|
||||||
|
value!({
|
||||||
|
"__type": {
|
||||||
|
"fields": [
|
||||||
|
{"name": "a"},
|
||||||
|
{"name": "b"},
|
||||||
|
{"name": "c"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let query = "{ obj { a b c } }";
|
||||||
|
assert_eq!(
|
||||||
|
schema.execute(query).await.data,
|
||||||
|
value!({
|
||||||
|
"obj": {
|
||||||
|
"a": 100,
|
||||||
|
"b": 200,
|
||||||
|
"c": 300,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user