Custom directive
This commit is contained in:
parent
3f458dcb31
commit
1b50738af7
|
@ -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.6] 2021-11-19
|
||||||
|
|
||||||
|
- Custom directives. [Book](https://async-graphql.github.io/async-graphql/en/custom_directive.html)
|
||||||
|
|
||||||
## [3.0.5] 2021-11-19
|
## [3.0.5] 2021-11-19
|
||||||
|
|
||||||
- Remove skipped fields from the document before executing the query.
|
- Remove skipped fields from the document before executing the query.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use darling::ast::{Data, Fields};
|
use darling::ast::{Data, Fields};
|
||||||
use darling::util::{Ignored, SpannedValue};
|
use darling::util::{Ignored, SpannedValue};
|
||||||
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant};
|
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant};
|
||||||
|
@ -697,3 +699,29 @@ pub struct ComplexObjectField {
|
||||||
pub visible: Option<Visible>,
|
pub visible: Option<Visible>,
|
||||||
pub complexity: Option<ComplexityType>,
|
pub complexity: Option<ComplexityType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromMeta, Default)]
|
||||||
|
#[darling(default)]
|
||||||
|
pub struct Directive {
|
||||||
|
pub internal: bool,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub visible: Option<Visible>,
|
||||||
|
pub repeatable: bool,
|
||||||
|
pub rename_args: Option<RenameRule>,
|
||||||
|
#[darling(multiple, rename = "location")]
|
||||||
|
pub locations: Vec<DirectiveLocation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, FromMeta)]
|
||||||
|
#[darling(rename_all = "lowercase")]
|
||||||
|
pub enum DirectiveLocation {
|
||||||
|
Field,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DirectiveLocation {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DirectiveLocation::Field => write!(f, "FIELD"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::ext::IdentExt;
|
||||||
|
use syn::{Error, FnArg, ItemFn, Pat};
|
||||||
|
|
||||||
|
use crate::args;
|
||||||
|
use crate::args::{Argument, RenameRuleExt, RenameTarget};
|
||||||
|
use crate::utils::{
|
||||||
|
generate_default, get_crate_name, get_rustdoc, parse_graphql_attrs, remove_graphql_attrs,
|
||||||
|
visible_fn, GeneratorResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn generate(
|
||||||
|
directive_args: &args::Directive,
|
||||||
|
item_fn: &mut ItemFn,
|
||||||
|
) -> GeneratorResult<TokenStream> {
|
||||||
|
let crate_name = get_crate_name(directive_args.internal);
|
||||||
|
let ident = &item_fn.sig.ident;
|
||||||
|
let vis = &item_fn.vis;
|
||||||
|
let directive_name = directive_args
|
||||||
|
.name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| item_fn.sig.ident.to_string());
|
||||||
|
let desc = get_rustdoc(&item_fn.attrs)?
|
||||||
|
.map(|s| quote!(::std::option::Option::Some(#s)))
|
||||||
|
.unwrap_or_else(|| quote!(::std::option::Option::None));
|
||||||
|
let visible = visible_fn(&directive_args.visible);
|
||||||
|
let repeatable = directive_args.repeatable;
|
||||||
|
|
||||||
|
let mut get_params = Vec::new();
|
||||||
|
let mut use_params = Vec::new();
|
||||||
|
let mut schema_args = Vec::new();
|
||||||
|
|
||||||
|
for arg in item_fn.sig.inputs.iter_mut() {
|
||||||
|
let mut arg_info = None;
|
||||||
|
|
||||||
|
if let FnArg::Typed(pat) = arg {
|
||||||
|
if let Pat::Ident(ident) = &*pat.pat {
|
||||||
|
arg_info = Some((ident.clone(), pat.ty.clone(), pat.attrs.clone()));
|
||||||
|
remove_graphql_attrs(&mut pat.attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (arg_ident, arg_ty, arg_attrs) = match arg_info {
|
||||||
|
Some(info) => info,
|
||||||
|
None => {
|
||||||
|
return Err(Error::new_spanned(arg, "Invalid argument type.").into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Argument {
|
||||||
|
name,
|
||||||
|
desc,
|
||||||
|
default,
|
||||||
|
default_with,
|
||||||
|
validator,
|
||||||
|
visible,
|
||||||
|
secret,
|
||||||
|
..
|
||||||
|
} = parse_graphql_attrs::<args::Argument>(&arg_attrs)?.unwrap_or_default();
|
||||||
|
|
||||||
|
let name = name.clone().unwrap_or_else(|| {
|
||||||
|
directive_args
|
||||||
|
.rename_args
|
||||||
|
.rename(arg_ident.ident.unraw().to_string(), RenameTarget::Argument)
|
||||||
|
});
|
||||||
|
let desc = desc
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| quote! {::std::option::Option::Some(#s)})
|
||||||
|
.unwrap_or_else(|| quote! {::std::option::Option::None});
|
||||||
|
let default = generate_default(&default, &default_with)?;
|
||||||
|
let schema_default = default
|
||||||
|
.as_ref()
|
||||||
|
.map(|value| {
|
||||||
|
quote! {
|
||||||
|
::std::option::Option::Some(::std::string::ToString::to_string(
|
||||||
|
&<#arg_ty as #crate_name::InputType>::to_value(&#value)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| quote! {::std::option::Option::None});
|
||||||
|
let visible = visible_fn(&visible);
|
||||||
|
|
||||||
|
schema_args.push(quote! {
|
||||||
|
args.insert(#name, #crate_name::registry::MetaInputValue {
|
||||||
|
name: #name,
|
||||||
|
description: #desc,
|
||||||
|
ty: <#arg_ty as #crate_name::InputType>::create_type_info(registry),
|
||||||
|
default_value: #schema_default,
|
||||||
|
visible: #visible,
|
||||||
|
is_secret: #secret,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let validators = validator.clone().unwrap_or_default().create_validators(
|
||||||
|
&crate_name,
|
||||||
|
quote!(&#arg_ident),
|
||||||
|
quote!(ty),
|
||||||
|
Some(quote!(.map_err(|err| err.into_server_error(__pos)))),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let default = match default {
|
||||||
|
Some(default) => {
|
||||||
|
quote! { ::std::option::Option::Some(|| -> #arg_ty { #default }) }
|
||||||
|
}
|
||||||
|
None => quote! { ::std::option::Option::None },
|
||||||
|
};
|
||||||
|
get_params.push(quote! {
|
||||||
|
let (__pos, #arg_ident) = ctx.param_value::<#arg_ty>(#name, #default)?;
|
||||||
|
#validators
|
||||||
|
});
|
||||||
|
|
||||||
|
use_params.push(quote! { #arg_ident });
|
||||||
|
}
|
||||||
|
|
||||||
|
let locations = directive_args
|
||||||
|
.locations
|
||||||
|
.iter()
|
||||||
|
.map(|loc| {
|
||||||
|
let loc = quote::format_ident!("{}", loc.to_string());
|
||||||
|
quote!(#crate_name::registry::__DirectiveLocation::#loc)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if locations.is_empty() {
|
||||||
|
return Err(Error::new(
|
||||||
|
ident.span(),
|
||||||
|
"At least one location is required for the directive.",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#vis struct #ident;
|
||||||
|
|
||||||
|
#[#crate_name::async_trait::async_trait]
|
||||||
|
impl #crate_name::CustomDirectiveFactory for #ident {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
#directive_name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&self, registry: &mut #crate_name::registry::Registry) {
|
||||||
|
let meta = #crate_name::registry::MetaDirective {
|
||||||
|
name: #directive_name,
|
||||||
|
description: #desc,
|
||||||
|
locations: vec![#(#locations),*],
|
||||||
|
args: {
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut args = #crate_name::indexmap::IndexMap::new();
|
||||||
|
#(#schema_args)*
|
||||||
|
args
|
||||||
|
},
|
||||||
|
is_repeatable: #repeatable,
|
||||||
|
visible: #visible,
|
||||||
|
};
|
||||||
|
registry.add_directive(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(
|
||||||
|
&self,
|
||||||
|
ctx: &#crate_name::ContextDirective<'_>,
|
||||||
|
directive: &#crate_name::parser::types::Directive,
|
||||||
|
) -> #crate_name::ServerResult<::std::boxed::Box<dyn #crate_name::CustomDirective>> {
|
||||||
|
#item_fn
|
||||||
|
|
||||||
|
#(#get_params)*
|
||||||
|
Ok(::std::boxed::Box::new(#ident(#(#use_params),*)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(expanded.into())
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ extern crate proc_macro;
|
||||||
mod args;
|
mod args;
|
||||||
mod complex_object;
|
mod complex_object;
|
||||||
mod description;
|
mod description;
|
||||||
|
mod directive;
|
||||||
mod r#enum;
|
mod r#enum;
|
||||||
mod input_object;
|
mod input_object;
|
||||||
mod interface;
|
mod interface;
|
||||||
|
@ -25,7 +26,7 @@ mod validators;
|
||||||
use darling::{FromDeriveInput, FromMeta};
|
use darling::{FromDeriveInput, FromMeta};
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syn::parse_macro_input;
|
use syn::parse_macro_input;
|
||||||
use syn::{AttributeArgs, DeriveInput, ItemImpl};
|
use syn::{AttributeArgs, DeriveInput, ItemFn, ItemImpl};
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -201,3 +202,18 @@ pub fn derive_newtype(input: TokenStream) -> TokenStream {
|
||||||
Err(err) => err.write_errors().into(),
|
Err(err) => err.write_errors().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn Directive(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
let directive_args =
|
||||||
|
match args::Directive::from_list(&parse_macro_input!(args as AttributeArgs)) {
|
||||||
|
Ok(directive_args) => directive_args,
|
||||||
|
Err(err) => return TokenStream::from(err.write_errors()),
|
||||||
|
};
|
||||||
|
let mut item_fn = parse_macro_input!(input as ItemFn);
|
||||||
|
match directive::generate(&directive_args, &mut item_fn) {
|
||||||
|
Ok(expanded) => expanded,
|
||||||
|
Err(err) => err.write_errors().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -160,6 +160,10 @@ impl Validators {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if codes.is_empty() {
|
||||||
|
return Ok(quote!());
|
||||||
|
}
|
||||||
|
|
||||||
let codes = codes.into_iter().map(|s| quote!(#s #map_err ?));
|
let codes = codes.into_iter().map(|s| quote!(#s #map_err ?));
|
||||||
|
|
||||||
if self.list {
|
if self.list {
|
||||||
|
|
|
@ -37,4 +37,5 @@
|
||||||
- [Advanced topics](advanced_topics.md)
|
- [Advanced topics](advanced_topics.md)
|
||||||
- [Custom scalars](custom_scalars.md)
|
- [Custom scalars](custom_scalars.md)
|
||||||
- [Optimizing N+1 queries](dataloader.md)
|
- [Optimizing N+1 queries](dataloader.md)
|
||||||
|
- [Custom directive](custom_directive.md)
|
||||||
- [Apollo Federation](apollo_federation.md)
|
- [Apollo Federation](apollo_federation.md)
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Custom directive
|
||||||
|
|
||||||
|
`Async-graphql` can easily customize directives, which can extend the behavior of GraphQL.
|
||||||
|
|
||||||
|
To create a custom directive, you need to implement the `CustomDirective` trait, and then use the `Directive` macro to
|
||||||
|
generate a factory function that receives the parameters of the directive and returns an instance of the directive.
|
||||||
|
|
||||||
|
Currently `Async-graphql` only supports directive located at `FIELD`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct ConcatDirective {
|
||||||
|
value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl CustomDirective for ConcatDirective {
|
||||||
|
async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult<Option<Value>> {
|
||||||
|
resolve.await.map(|value| {
|
||||||
|
value.map(|value| match value {
|
||||||
|
Value::String(str) => Value::String(str + &self.value),
|
||||||
|
_ => value,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Directive(location = "field")]
|
||||||
|
fn concat(value: String) -> impl CustomDirective {
|
||||||
|
ConcatDirective { value }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Register the directive when building the schema:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
|
||||||
|
.directive(concat)
|
||||||
|
.finish();
|
||||||
|
```
|
|
@ -36,5 +36,5 @@
|
||||||
- [高级主题](advanced_topics.md)
|
- [高级主题](advanced_topics.md)
|
||||||
- [自定义标量](custom_scalars.md)
|
- [自定义标量](custom_scalars.md)
|
||||||
- [优化查询(解决N+1问题)](dataloader.md)
|
- [优化查询(解决N+1问题)](dataloader.md)
|
||||||
- [自定义扩展](custom_extensions.md)
|
- [自定义指令](custom_directive.md)
|
||||||
- [Apollo Federation集成](apollo_federation.md)
|
- [Apollo Federation集成](apollo_federation.md)
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# 自定义指令
|
||||||
|
|
||||||
|
`Async-graphql`可以很方便的自定义指令,这可以扩展GraphQL的行为。
|
||||||
|
|
||||||
|
创建一个自定义指令,需要实现 `CustomDirective` trait,然后用`Directive`宏生成一个工厂函数,该函数接收指令的参数并返回指令的实例。
|
||||||
|
|
||||||
|
目前`Async-graphql`仅支持添加`FIELD`位置的指令。
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct ConcatDirective {
|
||||||
|
value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl CustomDirective for ConcatDirective {
|
||||||
|
async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult<Option<Value>> {
|
||||||
|
resolve.await.map(|value| {
|
||||||
|
value.map(|value| match value {
|
||||||
|
Value::String(str) => Value::String(str + &self.value),
|
||||||
|
_ => value,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Directive(location = "field")]
|
||||||
|
fn concat(value: String) -> impl CustomDirective {
|
||||||
|
ConcatDirective { value }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
创建模式时注册指令:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
|
||||||
|
.directive(concat)
|
||||||
|
.finish();
|
||||||
|
```
|
|
@ -1,7 +0,0 @@
|
||||||
# 自定义扩展
|
|
||||||
|
|
||||||
一个GraphQL扩展对象能够接收一个查询执行各个阶段的事件,你可以收集想要的数据,这些数据能够在查询结果中返回。
|
|
||||||
|
|
||||||
只需要实现`async_graphql::Extension`就能够定义一个扩展对象,然后在创建`Schema`的时候调用`Schema::extension`应用扩展。
|
|
||||||
|
|
||||||
你可以参考[Apollo tracing](https://github.com/async-graphql/async-graphql/blob/master/src/extensions/tracing.rs)来实现自己的扩展类型。
|
|
|
@ -17,6 +17,7 @@ Comparing Features of Other Rust GraphQL Implementations
|
||||||
| Dataloading | 👍 | 👍 |
|
| Dataloading | 👍 | 👍 |
|
||||||
| Custom Scalar | 👍 | 👍 |
|
| Custom Scalar | 👍 | 👍 |
|
||||||
| Custom Error | 👍 | 👍 |
|
| Custom Error | 👍 | 👍 |
|
||||||
|
| Custom Directive | 👍 | ⛔ |
|
||||||
| Extensions | 👍 | ⛔️ |
|
| Extensions | 👍 | ⛔️ |
|
||||||
| Cursor Connections | 👍 | ⛔️ |
|
| Cursor Connections | 👍 | ⛔️ |
|
||||||
| Query complexity/depth | 👍 | ⛔️ |
|
| Query complexity/depth | 👍 | ⛔️ |
|
||||||
|
|
|
@ -14,7 +14,7 @@ use serde::Serialize;
|
||||||
|
|
||||||
use crate::extensions::Extensions;
|
use crate::extensions::Extensions;
|
||||||
use crate::parser::types::{
|
use crate::parser::types::{
|
||||||
Field, FragmentDefinition, OperationDefinition, Selection, SelectionSet,
|
Directive, Field, FragmentDefinition, OperationDefinition, Selection, SelectionSet,
|
||||||
};
|
};
|
||||||
use crate::schema::SchemaEnv;
|
use crate::schema::SchemaEnv;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -59,6 +59,9 @@ pub type ContextSelectionSet<'a> = ContextBase<'a, &'a Positioned<SelectionSet>>
|
||||||
/// Context object for resolve field
|
/// Context object for resolve field
|
||||||
pub type Context<'a> = ContextBase<'a, &'a Positioned<Field>>;
|
pub type Context<'a> = ContextBase<'a, &'a Positioned<Field>>;
|
||||||
|
|
||||||
|
/// Context object for execute directive.
|
||||||
|
pub type ContextDirective<'a> = ContextBase<'a, &'a Positioned<Directive>>;
|
||||||
|
|
||||||
/// A segment in the path to the current query.
|
/// A segment in the path to the current query.
|
||||||
///
|
///
|
||||||
/// This is a borrowed form of [`PathSegment`](enum.PathSegment.html) used during execution instead
|
/// This is a borrowed form of [`PathSegment`](enum.PathSegment.html) used during execution instead
|
||||||
|
@ -497,6 +500,32 @@ impl<'a, T> ContextBase<'a, T> {
|
||||||
.node
|
.node
|
||||||
.into_const_with(|name| self.var_value(&name, pos))
|
.into_const_with(|name| self.var_value(&name, pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn get_param_value<Q: InputType>(
|
||||||
|
&self,
|
||||||
|
arguments: &[(Positioned<Name>, Positioned<InputValue>)],
|
||||||
|
name: &str,
|
||||||
|
default: Option<fn() -> Q>,
|
||||||
|
) -> ServerResult<(Pos, Q)> {
|
||||||
|
let value = arguments
|
||||||
|
.iter()
|
||||||
|
.find(|(n, _)| n.node.as_str() == name)
|
||||||
|
.map(|(_, value)| value)
|
||||||
|
.cloned();
|
||||||
|
if value.is_none() {
|
||||||
|
if let Some(default) = default {
|
||||||
|
return Ok((Pos::default(), default()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (pos, value) = match value {
|
||||||
|
Some(value) => (value.pos, Some(self.resolve_input_value(value)?)),
|
||||||
|
None => (Pos::default(), None),
|
||||||
|
};
|
||||||
|
InputType::parse(value)
|
||||||
|
.map(|value| (pos, value))
|
||||||
|
.map_err(|e| e.into_server_error(pos))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContextBase<'a, &'a Positioned<SelectionSet>> {
|
impl<'a> ContextBase<'a, &'a Positioned<SelectionSet>> {
|
||||||
|
@ -521,19 +550,7 @@ impl<'a> ContextBase<'a, &'a Positioned<Field>> {
|
||||||
name: &str,
|
name: &str,
|
||||||
default: Option<fn() -> T>,
|
default: Option<fn() -> T>,
|
||||||
) -> ServerResult<(Pos, T)> {
|
) -> ServerResult<(Pos, T)> {
|
||||||
let value = self.item.node.get_argument(name).cloned();
|
self.get_param_value(&self.item.node.arguments, name, default)
|
||||||
if value.is_none() {
|
|
||||||
if let Some(default) = default {
|
|
||||||
return Ok((Pos::default(), default()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let (pos, value) = match value {
|
|
||||||
Some(value) => (value.pos, Some(self.resolve_input_value(value)?)),
|
|
||||||
None => (Pos::default(), None),
|
|
||||||
};
|
|
||||||
InputType::parse(value)
|
|
||||||
.map(|value| (pos, value))
|
|
||||||
.map_err(|e| e.into_server_error(pos))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a uniform interface to inspect the forthcoming selections.
|
/// Creates a uniform interface to inspect the forthcoming selections.
|
||||||
|
@ -618,6 +635,17 @@ impl<'a> ContextBase<'a, &'a Positioned<Field>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ContextBase<'a, &'a Positioned<Directive>> {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn param_value<T: InputType>(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
default: Option<fn() -> T>,
|
||||||
|
) -> ServerResult<(Pos, T)> {
|
||||||
|
self.get_param_value(&self.item.node.arguments, name, default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Selection field.
|
/// Selection field.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct SelectionField<'a> {
|
pub struct SelectionField<'a> {
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::extensions::ResolveFut;
|
||||||
|
use crate::parser::types::Directive;
|
||||||
|
use crate::registry::Registry;
|
||||||
|
use crate::{Context, ContextDirective, ServerResult, Value};
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait CustomDirectiveFactory: Send + Sync + 'static {
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
|
fn register(&self, registry: &mut Registry);
|
||||||
|
|
||||||
|
fn create(
|
||||||
|
&self,
|
||||||
|
ctx: &ContextDirective<'_>,
|
||||||
|
directive: &Directive,
|
||||||
|
) -> ServerResult<Box<dyn CustomDirective>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a custom directive.
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub trait CustomDirective: Sync + Send + 'static {
|
||||||
|
/// Called at resolve field.
|
||||||
|
async fn resolve_field(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
resolve: ResolveFut<'_>,
|
||||||
|
) -> ServerResult<Option<Value>> {
|
||||||
|
resolve.await
|
||||||
|
}
|
||||||
|
}
|
|
@ -120,7 +120,8 @@ type ValidationFut<'a> =
|
||||||
|
|
||||||
type ExecuteFut<'a> = &'a mut (dyn Future<Output = Response> + Send + Unpin);
|
type ExecuteFut<'a> = &'a mut (dyn Future<Output = Response> + Send + Unpin);
|
||||||
|
|
||||||
type ResolveFut<'a> = &'a mut (dyn Future<Output = ServerResult<Option<Value>>> + Send + Unpin);
|
/// A future type used to resolve the field
|
||||||
|
pub type ResolveFut<'a> = &'a mut (dyn Future<Output = ServerResult<Option<Value>>> + Send + Unpin);
|
||||||
|
|
||||||
/// The remainder of a extension chain for request.
|
/// The remainder of a extension chain for request.
|
||||||
pub struct NextRequest<'a> {
|
pub struct NextRequest<'a> {
|
||||||
|
|
225
src/lib.rs
225
src/lib.rs
|
@ -164,6 +164,7 @@
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
|
||||||
mod base;
|
mod base;
|
||||||
|
mod custom_directive;
|
||||||
mod error;
|
mod error;
|
||||||
mod guard;
|
mod guard;
|
||||||
mod look_ahead;
|
mod look_ahead;
|
||||||
|
@ -212,10 +213,12 @@ pub use base::{
|
||||||
ComplexObject, Description, InputObjectType, InputType, InterfaceType, ObjectType, OutputType,
|
ComplexObject, Description, InputObjectType, InputType, InterfaceType, ObjectType, OutputType,
|
||||||
UnionType,
|
UnionType,
|
||||||
};
|
};
|
||||||
|
pub use custom_directive::{CustomDirective, CustomDirectiveFactory};
|
||||||
pub use error::{
|
pub use error::{
|
||||||
Error, ErrorExtensionValues, ErrorExtensions, InputValueError, InputValueResult,
|
Error, ErrorExtensionValues, ErrorExtensions, InputValueError, InputValueResult,
|
||||||
ParseRequestError, PathSegment, Result, ResultExt, ServerError, ServerResult,
|
ParseRequestError, PathSegment, Result, ResultExt, ServerError, ServerResult,
|
||||||
};
|
};
|
||||||
|
pub use extensions::ResolveFut;
|
||||||
pub use guard::{Guard, GuardExt};
|
pub use guard::{Guard, GuardExt};
|
||||||
pub use look_ahead::Lookahead;
|
pub use look_ahead::Lookahead;
|
||||||
pub use registry::CacheControl;
|
pub use registry::CacheControl;
|
||||||
|
@ -246,7 +249,7 @@ pub type FieldResult<T> = Result<T>;
|
||||||
///
|
///
|
||||||
/// All methods are converted to camelCase.
|
/// All methods are converted to camelCase.
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -258,8 +261,9 @@ pub type FieldResult<T> = Result<T>;
|
||||||
/// | use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.html) | bool | Y |
|
/// | use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.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 | 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 |
|
||||||
|
/// | serial | Resolve each field sequentially. | bool | Y |
|
||||||
///
|
///
|
||||||
/// # Field parameters
|
/// # Field attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -275,8 +279,11 @@ pub type FieldResult<T> = Result<T>;
|
||||||
/// | 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 |
|
||||||
|
/// | 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 |
|
||||||
|
/// | derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y |
|
||||||
///
|
///
|
||||||
/// # Field argument parameters
|
/// # Field argument attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|------------------------------------------|------------ |----------|
|
/// |--------------|------------------------------------------|------------ |----------|
|
||||||
|
@ -285,17 +292,13 @@ pub type FieldResult<T> = Result<T>;
|
||||||
/// | 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 |
|
||||||
/// | default_with | Expression to generate default value | code string | Y |
|
/// | default_with | Expression to generate default value | code string | Y |
|
||||||
/// | derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y |
|
|
||||||
/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y |
|
/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | 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 |
|
|
||||||
/// | 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 |
|
||||||
/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | 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 |
|
/// | key | Is entity key(for Federation) | bool | Y |
|
||||||
///
|
///
|
||||||
/// # Derived argument parameters
|
/// # Derived argument attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|------------------------------------------|------------ |----------|
|
/// |--------------|------------------------------------------|------------ |----------|
|
||||||
|
@ -322,7 +325,7 @@ pub type FieldResult<T> = Result<T>;
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// async fn value(&self, ctx: &Context<'_>) -> { ... }
|
/// async fn value(&self, ctx: &Context<'_>) -> { ... }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -334,12 +337,12 @@ pub type FieldResult<T> = Result<T>;
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use async_graphql::*;
|
/// use async_graphql::*;
|
||||||
///
|
///
|
||||||
/// struct QueryRoot {
|
/// struct Query {
|
||||||
/// value: i32,
|
/// value: i32,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// /// value
|
/// /// value
|
||||||
/// async fn value(&self) -> i32 {
|
/// async fn value(&self) -> i32 {
|
||||||
/// self.value
|
/// self.value
|
||||||
|
@ -361,7 +364,7 @@ pub type FieldResult<T> = Result<T>;
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::new(QueryRoot { value: 10 }, EmptyMutation, EmptySubscription);
|
/// let schema = Schema::new(Query { value: 10 }, EmptyMutation, EmptySubscription);
|
||||||
/// let res = schema.execute(r#"{
|
/// let res = schema.execute(r#"{
|
||||||
/// value
|
/// value
|
||||||
/// valueRef
|
/// valueRef
|
||||||
|
@ -406,10 +409,10 @@ pub type FieldResult<T> = Result<T>;
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// struct QueryRoot;
|
/// struct Query;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// async fn objs(&self) -> Vec<Box<dyn MyTrait>> {
|
/// async fn objs(&self) -> Vec<Box<dyn MyTrait>> {
|
||||||
/// vec![
|
/// vec![
|
||||||
/// Box::new(MyObj("a".to_string())),
|
/// Box::new(MyObj("a".to_string())),
|
||||||
|
@ -419,7 +422,7 @@ pub type FieldResult<T> = Result<T>;
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
/// let res = schema.execute("{ objs { name } }").await.into_result().unwrap().data;
|
/// let res = schema.execute("{ objs { name } }").await.into_result().unwrap().data;
|
||||||
/// assert_eq!(res, value!({
|
/// assert_eq!(res, value!({
|
||||||
/// "objs": [
|
/// "objs": [
|
||||||
|
@ -437,7 +440,7 @@ pub use async_graphql_derive::Object;
|
||||||
///
|
///
|
||||||
/// Similar to `Object`, but defined on a structure that automatically generates getters for all fields. For a list of valid field types, see [`Object`](attr.Object.html). All fields are converted to camelCase.
|
/// Similar to `Object`, but defined on a structure that automatically generates getters for all fields. For a list of valid field types, see [`Object`](attr.Object.html). All fields are converted to camelCase.
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -450,7 +453,7 @@ pub use async_graphql_derive::Object;
|
||||||
/// | 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 |
|
/// | 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 |
|
/// | serial | Resolve each field sequentially. | bool | Y |
|
||||||
///
|
///
|
||||||
/// # Field parameters
|
/// # Field attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -458,7 +461,7 @@ pub use async_graphql_derive::Object;
|
||||||
/// | 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 |
|
||||||
/// | 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 |
|
||||||
/// | owned | Field resolver return a ownedship value | bool | Y |
|
/// | owned | Field resolver return a ownedship value | bool | Y |
|
||||||
/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
||||||
/// | external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y |
|
/// | external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y |
|
||||||
|
@ -468,7 +471,7 @@ pub use async_graphql_derive::Object;
|
||||||
/// | 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 |
|
||||||
///
|
///
|
||||||
/// # Derived argument parameters
|
/// # Derived attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|------------------------------------------|------------ |----------|
|
/// |--------------|------------------------------------------|------------ |----------|
|
||||||
|
@ -484,12 +487,12 @@ pub use async_graphql_derive::Object;
|
||||||
/// use async_graphql::*;
|
/// use async_graphql::*;
|
||||||
///
|
///
|
||||||
/// #[derive(SimpleObject)]
|
/// #[derive(SimpleObject)]
|
||||||
/// struct QueryRoot {
|
/// struct Query {
|
||||||
/// value: i32,
|
/// value: i32,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::new(QueryRoot{ value: 10 }, EmptyMutation, EmptySubscription);
|
/// let schema = Schema::new(Query{ value: 10 }, EmptyMutation, EmptySubscription);
|
||||||
/// let res = schema.execute("{ value }").await.into_result().unwrap().data;
|
/// let res = schema.execute("{ value }").await.into_result().unwrap().data;
|
||||||
/// assert_eq!(res, value!({
|
/// assert_eq!(res, value!({
|
||||||
/// "value": 10,
|
/// "value": 10,
|
||||||
|
@ -508,7 +511,7 @@ pub use async_graphql_derive::SimpleObject;
|
||||||
/// But this can be done more beautifully with the `ComplexObject` macro. We can use the `SimpleObject` macro to define
|
/// But this can be done more beautifully with the `ComplexObject` macro. We can use the `SimpleObject` macro to define
|
||||||
/// some simple fields, and use the `ComplexObject` macro to define some other fields that need to be calculated.
|
/// some simple fields, and use the `ComplexObject` macro to define some other fields that need to be calculated.
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -516,15 +519,15 @@ pub use async_graphql_derive::SimpleObject;
|
||||||
/// | 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 |
|
||||||
///
|
///
|
||||||
/// # Field parameters
|
/// # 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 |
|
||||||
/// | deprecation | Field deprecated | bool | Y |
|
/// | deprecation | Field deprecated | bool | Y |
|
||||||
/// | deprecation | Field deprecation reason | string | Y |
|
/// | deprecation | Field deprecation reason | string | Y |
|
||||||
/// | derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y |
|
|
||||||
/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
/// | cache_control | Field cache control | [`CacheControl`](struct.CacheControl.html) | Y |
|
||||||
/// | external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y |
|
/// | external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y |
|
||||||
/// | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y |
|
/// | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y |
|
||||||
|
@ -532,15 +535,23 @@ pub use async_graphql_derive::SimpleObject;
|
||||||
/// | 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 |
|
||||||
/// | secret | Mark this field as a secret, it will not output the actual value in the log. | 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 |
|
||||||
|
/// | derived | Generate derived fields *[See also the Book](https://async-graphql.github.io/async-graphql/en/derived_fields.html).* | object | Y |
|
||||||
///
|
///
|
||||||
/// # Derived argument parameters
|
/// # Field argument attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|------------------------------------------|------------ |----------|
|
/// |--------------|------------------------------------------|------------ |----------|
|
||||||
/// | name | Generated derived field name | string | N |
|
/// | name | Argument name | string | Y |
|
||||||
/// | into | Type to derived an into | string | Y |
|
/// | desc | Argument description | string | Y |
|
||||||
/// | with | Function to apply to manage advanced use cases | string| Y |
|
/// | default | Use `Default::default` for default value | none | Y |
|
||||||
|
/// | default | Argument default value | literal | Y |
|
||||||
|
/// | default_with | Expression to generate default value | code string | Y |
|
||||||
|
/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | 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 |
|
||||||
|
/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y |
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -561,17 +572,17 @@ pub use async_graphql_derive::SimpleObject;
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// struct QueryRoot;
|
/// struct Query;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// async fn obj(&self) -> MyObj {
|
/// async fn obj(&self) -> MyObj {
|
||||||
/// MyObj { a: 10, b: 20 }
|
/// MyObj { a: 10, b: 20 }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
/// let res = schema.execute("{ obj { a b c } }").await.into_result().unwrap().data;
|
/// let res = schema.execute("{ obj { a b c } }").await.into_result().unwrap().data;
|
||||||
/// assert_eq!(res, value!({
|
/// assert_eq!(res, value!({
|
||||||
/// "obj": {
|
/// "obj": {
|
||||||
|
@ -588,7 +599,7 @@ pub use async_graphql_derive::ComplexObject;
|
||||||
///
|
///
|
||||||
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_enum.html).*
|
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_enum.html).*
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|---------------------------|----------|----------|
|
/// |--------------|---------------------------|----------|----------|
|
||||||
|
@ -598,7 +609,7 @@ pub use async_graphql_derive::ComplexObject;
|
||||||
/// | 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 |
|
||||||
///
|
///
|
||||||
/// # Item parameters
|
/// # Item attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |-------------|---------------------------|----------|----------|
|
/// |-------------|---------------------------|----------|----------|
|
||||||
|
@ -619,13 +630,13 @@ pub use async_graphql_derive::ComplexObject;
|
||||||
/// #[graphql(name = "b")] B,
|
/// #[graphql(name = "b")] B,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// struct QueryRoot {
|
/// struct Query {
|
||||||
/// value1: MyEnum,
|
/// value1: MyEnum,
|
||||||
/// value2: MyEnum,
|
/// value2: MyEnum,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// /// value1
|
/// /// value1
|
||||||
/// async fn value1(&self) -> MyEnum {
|
/// async fn value1(&self) -> MyEnum {
|
||||||
/// self.value1
|
/// self.value1
|
||||||
|
@ -638,7 +649,7 @@ pub use async_graphql_derive::ComplexObject;
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::new(QueryRoot{ value1: MyEnum::A, value2: MyEnum::B }, EmptyMutation, EmptySubscription);
|
/// let schema = Schema::new(Query{ value1: MyEnum::A, value2: MyEnum::B }, EmptyMutation, EmptySubscription);
|
||||||
/// let res = schema.execute("{ value1 value2 }").await.into_result().unwrap().data;
|
/// let res = schema.execute("{ value1 value2 }").await.into_result().unwrap().data;
|
||||||
/// assert_eq!(res, value!({ "value1": "A", "value2": "b" }));
|
/// assert_eq!(res, value!({ "value1": "A", "value2": "b" }));
|
||||||
/// });
|
/// });
|
||||||
|
@ -649,7 +660,7 @@ pub use async_graphql_derive::Enum;
|
||||||
///
|
///
|
||||||
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_input_object.html).*
|
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_input_object.html).*
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -658,7 +669,7 @@ pub use async_graphql_derive::Enum;
|
||||||
/// | 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 |
|
||||||
///
|
///
|
||||||
/// # Field parameters
|
/// # Field attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|------------------------------------------|-------------|----------|
|
/// |--------------|------------------------------------------|-------------|----------|
|
||||||
|
@ -685,10 +696,10 @@ pub use async_graphql_derive::Enum;
|
||||||
/// b: i32,
|
/// b: i32,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// struct QueryRoot;
|
/// struct Query;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// /// value
|
/// /// value
|
||||||
/// async fn value(&self, input: MyInputObject) -> i32 {
|
/// async fn value(&self, input: MyInputObject) -> i32 {
|
||||||
/// input.a * input.b
|
/// input.a * input.b
|
||||||
|
@ -696,7 +707,7 @@ pub use async_graphql_derive::Enum;
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||||
/// let res = schema.execute(r#"
|
/// let res = schema.execute(r#"
|
||||||
/// {
|
/// {
|
||||||
/// value1: value(input:{a:9, b:3})
|
/// value1: value(input:{a:9, b:3})
|
||||||
|
@ -711,7 +722,7 @@ pub use async_graphql_derive::InputObject;
|
||||||
///
|
///
|
||||||
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_interface.html).*
|
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_interface.html).*
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -723,7 +734,7 @@ pub use async_graphql_derive::InputObject;
|
||||||
/// | 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 |
|
||||||
///
|
///
|
||||||
/// # Field parameters
|
/// # Field attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |-------------|---------------------------|----------|----------|
|
/// |-------------|---------------------------|----------|----------|
|
||||||
|
@ -740,7 +751,7 @@ pub use async_graphql_derive::InputObject;
|
||||||
/// | 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 |
|
||||||
///
|
///
|
||||||
/// # Field argument parameters
|
/// # Field argument attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|------------------------------------------|-------------|----------|
|
/// |--------------|------------------------------------------|-------------|----------|
|
||||||
|
@ -817,17 +828,17 @@ pub use async_graphql_derive::InputObject;
|
||||||
/// TypeA(TypeA)
|
/// TypeA(TypeA)
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// struct QueryRoot;
|
/// struct Query;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// async fn type_a(&self) -> MyInterface {
|
/// async fn type_a(&self) -> MyInterface {
|
||||||
/// TypeA { value: 10 }.into()
|
/// TypeA { value: 10 }.into()
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
||||||
/// let res = schema.execute(r#"
|
/// let res = schema.execute(r#"
|
||||||
/// {
|
/// {
|
||||||
/// typeA {
|
/// typeA {
|
||||||
|
@ -853,7 +864,7 @@ pub use async_graphql_derive::Interface;
|
||||||
///
|
///
|
||||||
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_union.html).*
|
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_union.html).*
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |-------------|---------------------------|----------|----------|
|
/// |-------------|---------------------------|----------|----------|
|
||||||
|
@ -861,7 +872,7 @@ pub use async_graphql_derive::Interface;
|
||||||
/// | 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 |
|
||||||
///
|
///
|
||||||
/// # Item parameters
|
/// # Item attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|------------------------------------------|----------|----------|
|
/// |--------------|------------------------------------------|----------|----------|
|
||||||
|
@ -890,17 +901,17 @@ pub use async_graphql_derive::Interface;
|
||||||
/// TypeB(TypeB),
|
/// TypeB(TypeB),
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// struct QueryRoot;
|
/// struct Query;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// async fn all_data(&self) -> Vec<MyUnion> {
|
/// async fn all_data(&self) -> Vec<MyUnion> {
|
||||||
/// vec![TypeA { value_a: 10 }.into(), TypeB { value_b: 20 }.into()]
|
/// vec![TypeA { value_a: 10 }.into(), TypeB { value_b: 20 }.into()]
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
||||||
/// let res = schema.execute(r#"
|
/// let res = schema.execute(r#"
|
||||||
/// {
|
/// {
|
||||||
/// allData {
|
/// allData {
|
||||||
|
@ -931,7 +942,7 @@ pub use async_graphql_derive::Union;
|
||||||
/// Starting with the third parameter is one or more filtering conditions, The filter condition is the parameter of the field.
|
/// Starting with the third parameter is one or more filtering conditions, The filter condition is the parameter of the field.
|
||||||
/// The filter function should be synchronous.
|
/// The filter function should be synchronous.
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -943,7 +954,7 @@ pub use async_graphql_derive::Union;
|
||||||
/// | 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 |
|
||||||
/// | use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.html) | bool | Y |
|
/// | use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.html) | bool | Y |
|
||||||
///
|
///
|
||||||
/// # Field parameters
|
/// # Field attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |-------------|---------------------------|----------|----------|
|
/// |-------------|---------------------------|----------|----------|
|
||||||
|
@ -953,9 +964,11 @@ pub use async_graphql_derive::Union;
|
||||||
/// | 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 |
|
||||||
|
/// | 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 |
|
||||||
/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y |
|
/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y |
|
||||||
///
|
///
|
||||||
/// # Field argument parameters
|
/// # Field argument attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |--------------|------------------------------------------|------------ |----------|
|
/// |--------------|------------------------------------------|------------ |----------|
|
||||||
|
@ -988,7 +1001,7 @@ pub use async_graphql_derive::Subscription;
|
||||||
|
|
||||||
/// Define a Scalar
|
/// Define a Scalar
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |-------------|---------------------------|----------|----------|
|
/// |-------------|---------------------------|----------|----------|
|
||||||
|
@ -1001,7 +1014,7 @@ pub use async_graphql_derive::Scalar;
|
||||||
///
|
///
|
||||||
/// It also implements `From<InnerType>` and `Into<InnerType>`.
|
/// It also implements `From<InnerType>` and `Into<InnerType>`.
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |-------------|---------------------------|----------|----------|
|
/// |-------------|---------------------------|----------|----------|
|
||||||
|
@ -1021,10 +1034,10 @@ pub use async_graphql_derive::Scalar;
|
||||||
/// #[derive(NewType)]
|
/// #[derive(NewType)]
|
||||||
/// struct Weight(f64);
|
/// struct Weight(f64);
|
||||||
///
|
///
|
||||||
/// struct QueryRoot;
|
/// struct Query;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// async fn value(&self) -> Weight {
|
/// async fn value(&self) -> Weight {
|
||||||
/// Weight(1.234)
|
/// Weight(1.234)
|
||||||
/// }
|
/// }
|
||||||
|
@ -1035,7 +1048,7 @@ pub use async_graphql_derive::Scalar;
|
||||||
/// let weight_f64: f64 = weight.into();
|
/// let weight_f64: f64 = weight.into();
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
||||||
///
|
///
|
||||||
/// let res = schema.execute("{ value }").await.into_result().unwrap().data;
|
/// let res = schema.execute("{ value }").await.into_result().unwrap().data;
|
||||||
/// assert_eq!(res, value!({
|
/// assert_eq!(res, value!({
|
||||||
|
@ -1044,7 +1057,7 @@ pub use async_graphql_derive::Scalar;
|
||||||
///
|
///
|
||||||
/// let res = schema.execute(r#"
|
/// let res = schema.execute(r#"
|
||||||
/// {
|
/// {
|
||||||
/// __type(name: "QueryRoot") {
|
/// __type(name: "Query") {
|
||||||
/// fields {
|
/// fields {
|
||||||
/// name type {
|
/// name type {
|
||||||
/// kind
|
/// kind
|
||||||
|
@ -1079,17 +1092,17 @@ pub use async_graphql_derive::Scalar;
|
||||||
/// #[graphql(name)] // or: #[graphql(name = true)], #[graphql(name = "Weight")]
|
/// #[graphql(name)] // or: #[graphql(name = true)], #[graphql(name = "Weight")]
|
||||||
/// struct Weight(f64);
|
/// struct Weight(f64);
|
||||||
///
|
///
|
||||||
/// struct QueryRoot;
|
/// struct Query;
|
||||||
///
|
///
|
||||||
/// #[Object]
|
/// #[Object]
|
||||||
/// impl QueryRoot {
|
/// impl Query {
|
||||||
/// async fn value(&self) -> Weight {
|
/// async fn value(&self) -> Weight {
|
||||||
/// Weight(1.234)
|
/// Weight(1.234)
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
/// let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription).data("hello".to_string()).finish();
|
||||||
///
|
///
|
||||||
/// let res = schema.execute("{ value }").await.into_result().unwrap().data;
|
/// let res = schema.execute("{ value }").await.into_result().unwrap().data;
|
||||||
/// assert_eq!(res, value!({
|
/// assert_eq!(res, value!({
|
||||||
|
@ -1098,7 +1111,7 @@ pub use async_graphql_derive::Scalar;
|
||||||
///
|
///
|
||||||
/// let res = schema.execute(r#"
|
/// let res = schema.execute(r#"
|
||||||
/// {
|
/// {
|
||||||
/// __type(name: "QueryRoot") {
|
/// __type(name: "Query") {
|
||||||
/// fields {
|
/// fields {
|
||||||
/// name type {
|
/// name type {
|
||||||
/// kind
|
/// kind
|
||||||
|
@ -1135,7 +1148,7 @@ pub use async_graphql_derive::NewType;
|
||||||
///
|
///
|
||||||
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).*
|
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).*
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -1177,7 +1190,7 @@ pub use async_graphql_derive::MergedObject;
|
||||||
///
|
///
|
||||||
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).*
|
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/merging_objects.html).*
|
||||||
///
|
///
|
||||||
/// # Macro parameters
|
/// # Macro attributes
|
||||||
///
|
///
|
||||||
/// | Attribute | description | Type | Optional |
|
/// | Attribute | description | Type | Optional |
|
||||||
/// |---------------|---------------------------|----------|----------|
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
@ -1258,3 +1271,79 @@ pub use async_graphql_derive::MergedSubscription;
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub use async_graphql_derive::Description;
|
pub use async_graphql_derive::Description;
|
||||||
|
|
||||||
|
/// Define a directive for query.
|
||||||
|
///
|
||||||
|
/// *[See also the Book](https://async-graphql.github.io/async-graphql/en/custom_directive.html).*
|
||||||
|
///
|
||||||
|
/// # Macro attributes
|
||||||
|
///
|
||||||
|
/// | Attribute | description | Type | Optional |
|
||||||
|
/// |---------------|---------------------------|----------|----------|
|
||||||
|
/// | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
|
||||||
|
/// | repeatable | It means that the directive can be used multiple times in the same location. | bool | 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 |
|
||||||
|
/// | locations | Specify the location where the directive is available, multiples are allowed. The possible values is "field", ... | string | N |
|
||||||
|
///
|
||||||
|
/// # Directive attributes
|
||||||
|
///
|
||||||
|
/// | Attribute | description | Type | Optional |
|
||||||
|
/// |--------------|------------------------------------------|------------ |----------|
|
||||||
|
/// | name | Argument name | string | Y |
|
||||||
|
/// | desc | Argument description | string | Y |
|
||||||
|
/// | default | Use `Default::default` for default value | none | Y |
|
||||||
|
/// | default | Argument default value | literal | Y |
|
||||||
|
/// | default_with | Expression to generate default value | code string | Y |
|
||||||
|
/// | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | 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 |
|
||||||
|
/// | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y |
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use async_graphql::*;
|
||||||
|
///
|
||||||
|
/// struct ConcatDirective {
|
||||||
|
/// value: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[async_trait::async_trait]
|
||||||
|
/// impl CustomDirective for ConcatDirective {
|
||||||
|
/// async fn resolve_field(&self, _ctx: &Context<'_>, resolve: ResolveFut<'_>) -> ServerResult<Option<Value>> {
|
||||||
|
/// resolve.await.map(|value| {
|
||||||
|
/// value.map(|value| match value {
|
||||||
|
/// Value::String(str) => Value::String(str + &self.value),
|
||||||
|
/// _ => value,
|
||||||
|
/// })
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[Directive(location = "field")]
|
||||||
|
/// fn concat(value: String) -> impl CustomDirective {
|
||||||
|
/// ConcatDirective { value }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// struct Query;
|
||||||
|
///
|
||||||
|
/// #[Object]
|
||||||
|
/// impl Query {
|
||||||
|
/// async fn value(&self) -> &'static str {
|
||||||
|
/// "abc"
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// tokio::runtime::Runtime::new().unwrap().block_on(async move {
|
||||||
|
/// let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
|
||||||
|
/// .directive(concat)
|
||||||
|
/// .finish();
|
||||||
|
/// let res = schema.execute(r#"{ value @concat(value: "def") }"#).await.into_result().unwrap().data;
|
||||||
|
/// assert_eq!(res, value!({
|
||||||
|
/// "value": "abcdef",
|
||||||
|
/// }));
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
pub use async_graphql_derive::Directive;
|
||||||
|
|
|
@ -7,6 +7,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
use indexmap::set::IndexSet;
|
use indexmap::set::IndexSet;
|
||||||
|
|
||||||
|
pub use crate::model::__DirectiveLocation;
|
||||||
use crate::parser::types::{
|
use crate::parser::types::{
|
||||||
BaseType as ParsedBaseType, Field, Type as ParsedType, VariableDefinition,
|
BaseType as ParsedBaseType, Field, Type as ParsedType, VariableDefinition,
|
||||||
};
|
};
|
||||||
|
@ -350,6 +351,7 @@ pub struct MetaDirective {
|
||||||
pub locations: Vec<model::__DirectiveLocation>,
|
pub locations: Vec<model::__DirectiveLocation>,
|
||||||
pub args: IndexMap<&'static str, MetaInputValue>,
|
pub args: IndexMap<&'static str, MetaInputValue>,
|
||||||
pub is_repeatable: bool,
|
pub is_repeatable: bool,
|
||||||
|
pub visible: Option<MetaVisibleFn>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -763,6 +765,12 @@ impl Registry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for directive in self.directives.values() {
|
||||||
|
for arg in directive.args.values() {
|
||||||
|
traverse_input_value(&self.types, &mut used_types, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for type_name in Some(&self.query_type)
|
for type_name in Some(&self.query_type)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(self.mutation_type.iter())
|
.chain(self.mutation_type.iter())
|
||||||
|
@ -886,6 +894,14 @@ impl Registry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for directive in self.directives.values() {
|
||||||
|
if is_visible(ctx, &directive.visible) {
|
||||||
|
for arg in directive.args.values() {
|
||||||
|
traverse_input_value(ctx, &self.types, &mut visible_types, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for type_name in Some(&self.query_type)
|
for type_name in Some(&self.query_type)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(self.mutation_type.iter())
|
.chain(self.mutation_type.iter())
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use futures_util::FutureExt;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
@ -5,7 +6,9 @@ use indexmap::IndexMap;
|
||||||
|
|
||||||
use crate::extensions::ResolveInfo;
|
use crate::extensions::ResolveInfo;
|
||||||
use crate::parser::types::Selection;
|
use crate::parser::types::Selection;
|
||||||
use crate::{Context, ContextSelectionSet, Name, OutputType, ServerError, ServerResult, Value};
|
use crate::{
|
||||||
|
Context, ContextBase, ContextSelectionSet, Name, OutputType, ServerError, ServerResult, Value,
|
||||||
|
};
|
||||||
|
|
||||||
/// Represents a GraphQL container object.
|
/// Represents a GraphQL container object.
|
||||||
///
|
///
|
||||||
|
@ -152,14 +155,14 @@ impl<'a> Fields<'a> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.0.push(Box::pin({
|
let resolve_fut = Box::pin({
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
async move {
|
async move {
|
||||||
let ctx_field = ctx.with_field(field);
|
let ctx_field = ctx.with_field(field);
|
||||||
let field_name = ctx_field.item.node.response_key().node.clone();
|
let field_name = ctx_field.item.node.response_key().node.clone();
|
||||||
let extensions = &ctx.query_env.extensions;
|
let extensions = &ctx.query_env.extensions;
|
||||||
|
|
||||||
if extensions.is_empty() {
|
if extensions.is_empty() && field.node.directives.is_empty() {
|
||||||
Ok((
|
Ok((
|
||||||
field_name,
|
field_name,
|
||||||
root.resolve_field(&ctx_field).await?.unwrap_or_default(),
|
root.resolve_field(&ctx_field).await?.unwrap_or_default(),
|
||||||
|
@ -199,17 +202,57 @@ impl<'a> Fields<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let resolve_fut = root.resolve_field(&ctx_field);
|
let resolve_fut = root.resolve_field(&ctx_field);
|
||||||
futures_util::pin_mut!(resolve_fut);
|
|
||||||
Ok((
|
if field.node.directives.is_empty() {
|
||||||
field_name,
|
futures_util::pin_mut!(resolve_fut);
|
||||||
extensions
|
Ok((
|
||||||
.resolve(resolve_info, &mut resolve_fut)
|
field_name,
|
||||||
.await?
|
extensions
|
||||||
.unwrap_or_default(),
|
.resolve(resolve_info, &mut resolve_fut)
|
||||||
))
|
.await?
|
||||||
|
.unwrap_or_default(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
let mut resolve_fut = resolve_fut.boxed();
|
||||||
|
|
||||||
|
for directive in &field.node.directives {
|
||||||
|
if let Some(directive_factory) = ctx
|
||||||
|
.schema_env
|
||||||
|
.custom_directives
|
||||||
|
.get(directive.node.name.node.as_str())
|
||||||
|
{
|
||||||
|
let ctx_directive = ContextBase {
|
||||||
|
path_node: ctx_field.path_node,
|
||||||
|
item: directive,
|
||||||
|
schema_env: ctx_field.schema_env,
|
||||||
|
query_env: ctx_field.query_env,
|
||||||
|
};
|
||||||
|
let directive_instance = directive_factory
|
||||||
|
.create(&ctx_directive, &directive.node)?;
|
||||||
|
resolve_fut = Box::pin({
|
||||||
|
let ctx_field = ctx_field.clone();
|
||||||
|
async move {
|
||||||
|
directive_instance
|
||||||
|
.resolve_field(&ctx_field, &mut resolve_fut)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
field_name,
|
||||||
|
extensions
|
||||||
|
.resolve(resolve_info, &mut resolve_fut)
|
||||||
|
.await?
|
||||||
|
.unwrap_or_default(),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
self.0.push(resolve_fut);
|
||||||
}
|
}
|
||||||
selection => {
|
selection => {
|
||||||
let (type_condition, selection_set) = match selection {
|
let (type_condition, selection_set) = match selection {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -6,6 +7,7 @@ use futures_util::stream::{self, Stream, StreamExt};
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
|
|
||||||
use crate::context::{Data, QueryEnvInner};
|
use crate::context::{Data, QueryEnvInner};
|
||||||
|
use crate::custom_directive::CustomDirectiveFactory;
|
||||||
use crate::extensions::{ExtensionFactory, Extensions};
|
use crate::extensions::{ExtensionFactory, Extensions};
|
||||||
use crate::model::__DirectiveLocation;
|
use crate::model::__DirectiveLocation;
|
||||||
use crate::parser::types::{Directive, DocumentOperations, OperationType, Selection, SelectionSet};
|
use crate::parser::types::{Directive, DocumentOperations, OperationType, Selection, SelectionSet};
|
||||||
|
@ -31,6 +33,7 @@ pub struct SchemaBuilder<Query, Mutation, Subscription> {
|
||||||
complexity: Option<usize>,
|
complexity: Option<usize>,
|
||||||
depth: Option<usize>,
|
depth: Option<usize>,
|
||||||
extensions: Vec<Box<dyn ExtensionFactory>>,
|
extensions: Vec<Box<dyn ExtensionFactory>>,
|
||||||
|
custom_directives: HashMap<&'static str, Box<dyn CustomDirectiveFactory>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription> {
|
impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription> {
|
||||||
|
@ -131,6 +134,27 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register a custom directive.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the directive with the same name is already registered.
|
||||||
|
pub fn directive<T: CustomDirectiveFactory>(mut self, directive: T) -> Self {
|
||||||
|
let name = directive.name();
|
||||||
|
let instance = Box::new(directive);
|
||||||
|
|
||||||
|
instance.register(&mut self.registry);
|
||||||
|
|
||||||
|
if name == "skip"
|
||||||
|
|| name == "include"
|
||||||
|
|| self.custom_directives.insert(name, instance).is_some()
|
||||||
|
{
|
||||||
|
panic!("Directive `{}` already exists", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Build schema.
|
/// Build schema.
|
||||||
pub fn finish(mut self) -> Schema<Query, Mutation, Subscription> {
|
pub fn finish(mut self) -> Schema<Query, Mutation, Subscription> {
|
||||||
// federation
|
// federation
|
||||||
|
@ -149,6 +173,7 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
|
||||||
env: SchemaEnv(Arc::new(SchemaEnvInner {
|
env: SchemaEnv(Arc::new(SchemaEnvInner {
|
||||||
registry: self.registry,
|
registry: self.registry,
|
||||||
data: self.data,
|
data: self.data,
|
||||||
|
custom_directives: self.custom_directives,
|
||||||
})),
|
})),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -158,6 +183,7 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
|
||||||
pub struct SchemaEnvInner {
|
pub struct SchemaEnvInner {
|
||||||
pub registry: Registry,
|
pub registry: Registry,
|
||||||
pub data: Data,
|
pub data: Data,
|
||||||
|
pub custom_directives: HashMap<&'static str, Box<dyn CustomDirectiveFactory>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -244,6 +270,7 @@ where
|
||||||
complexity: None,
|
complexity: None,
|
||||||
depth: None,
|
depth: None,
|
||||||
extensions: Default::default(),
|
extensions: Default::default(),
|
||||||
|
custom_directives: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,6 +316,7 @@ where
|
||||||
args
|
args
|
||||||
},
|
},
|
||||||
is_repeatable: false,
|
is_repeatable: false,
|
||||||
|
visible: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
registry.add_directive(MetaDirective {
|
registry.add_directive(MetaDirective {
|
||||||
|
@ -312,6 +340,7 @@ where
|
||||||
args
|
args
|
||||||
},
|
},
|
||||||
is_repeatable: false,
|
is_repeatable: false,
|
||||||
|
visible: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
// register scalars
|
// register scalars
|
||||||
|
|
|
@ -76,3 +76,54 @@ pub async fn test_directive_include() {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn test_custom_directive() {
|
||||||
|
struct Concat {
|
||||||
|
prefix: String,
|
||||||
|
suffix: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl CustomDirective for Concat {
|
||||||
|
async fn resolve_field(
|
||||||
|
&self,
|
||||||
|
_ctx: &Context<'_>,
|
||||||
|
resolve: ResolveFut<'_>,
|
||||||
|
) -> ServerResult<Option<Value>> {
|
||||||
|
resolve.await.map(|value| {
|
||||||
|
value.map(|value| match value {
|
||||||
|
Value::String(str) => Value::String(self.prefix.clone() + &str + &self.suffix),
|
||||||
|
_ => value,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Directive(location = "field")]
|
||||||
|
fn concat(prefix: String, suffix: String) -> impl CustomDirective {
|
||||||
|
Concat { prefix, suffix }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct QueryRoot;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl QueryRoot {
|
||||||
|
pub async fn value(&self) -> &'static str {
|
||||||
|
"abc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
|
||||||
|
.directive(concat)
|
||||||
|
.finish();
|
||||||
|
assert_eq!(
|
||||||
|
schema
|
||||||
|
.execute(r#"{ value @concat(prefix: "&", suffix: "*") }"#)
|
||||||
|
.await
|
||||||
|
.into_result()
|
||||||
|
.unwrap()
|
||||||
|
.data,
|
||||||
|
value!({ "value": "&abc*" })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -261,6 +261,30 @@ pub async fn test_visible_fn() {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
pub async fn test_indirect_hiding_type() {
|
pub async fn test_indirect_hiding_type() {
|
||||||
|
#[derive(Enum, Eq, PartialEq, Copy, Clone)]
|
||||||
|
enum MyEnum1 {
|
||||||
|
A,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Enum, Eq, PartialEq, Copy, Clone)]
|
||||||
|
enum MyEnum2 {
|
||||||
|
A,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyDirective;
|
||||||
|
|
||||||
|
impl CustomDirective for MyDirective {}
|
||||||
|
|
||||||
|
#[Directive(location = "field")]
|
||||||
|
fn my_directive1(_a: MyEnum1) -> impl CustomDirective {
|
||||||
|
MyDirective
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Directive(location = "field", visible = false)]
|
||||||
|
fn my_directive2(_a: MyEnum2) -> impl CustomDirective {
|
||||||
|
MyDirective
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(SimpleObject)]
|
#[derive(SimpleObject)]
|
||||||
struct MyObj1 {
|
struct MyObj1 {
|
||||||
a: i32,
|
a: i32,
|
||||||
|
@ -380,7 +404,10 @@ pub async fn test_indirect_hiding_type() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
let schema = Schema::build(Query, EmptyMutation, EmptySubscription)
|
||||||
|
.directive(my_directive1)
|
||||||
|
.directive(my_directive2)
|
||||||
|
.finish();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
schema
|
schema
|
||||||
.execute(r#"{ __type(name: "MyObj1") { name } }"#)
|
.execute(r#"{ __type(name: "MyObj1") { name } }"#)
|
||||||
|
@ -530,6 +557,26 @@ pub async fn test_indirect_hiding_type() {
|
||||||
.data,
|
.data,
|
||||||
value!({ "__type": null })
|
value!({ "__type": null })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
schema
|
||||||
|
.execute(r#"{ __type(name: "MyEnum1") { name } }"#)
|
||||||
|
.await
|
||||||
|
.into_result()
|
||||||
|
.unwrap()
|
||||||
|
.data,
|
||||||
|
value!({ "__type": { "name": "MyEnum1" } })
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
schema
|
||||||
|
.execute(r#"{ __type(name: "MyEnum2") { name } }"#)
|
||||||
|
.await
|
||||||
|
.into_result()
|
||||||
|
.unwrap()
|
||||||
|
.data,
|
||||||
|
value!({ "__type": null })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
Loading…
Reference in New Issue