Fmt, clippy and remove post guard

This commit is contained in:
Aurelien Foucault 2020-10-06 00:17:29 +02:00
parent a935b867c7
commit 35a21d1994
8 changed files with 121 additions and 529 deletions

View File

@ -69,8 +69,6 @@ pub struct SimpleObjectField {
pub requires: Option<String>, pub requires: Option<String>,
#[darling(default)] #[darling(default)]
pub guard: Option<Meta>, pub guard: Option<Meta>,
#[darling(default)]
pub post_guard: Option<Meta>,
} }
#[derive(FromDeriveInput)] #[derive(FromDeriveInput)]
@ -123,7 +121,6 @@ pub struct ObjectField {
pub provides: Option<String>, pub provides: Option<String>,
pub requires: Option<String>, pub requires: Option<String>,
pub guard: Option<Meta>, pub guard: Option<Meta>,
pub post_guard: Option<Meta>,
} }
#[derive(FromDeriveInput)] #[derive(FromDeriveInput)]
@ -302,7 +299,6 @@ pub struct SubscriptionField {
pub name: Option<String>, pub name: Option<String>,
pub deprecation: Option<String>, pub deprecation: Option<String>,
pub guard: Option<Meta>, pub guard: Option<Meta>,
pub post_guard: Option<Meta>,
} }
#[derive(FromMeta, Default)] #[derive(FromMeta, Default)]

View File

@ -1,8 +1,8 @@
use crate::args; use crate::args;
use crate::output_type::OutputType; use crate::output_type::OutputType;
use crate::utils::{ use crate::utils::{
generate_default, generate_guards, generate_post_guards, generate_validator, get_cfg_attrs, generate_default, generate_guards, generate_validator, get_cfg_attrs, get_crate_name,
get_crate_name, get_param_getter_ident, get_rustdoc, parse_graphql_attrs, remove_graphql_attrs, get_param_getter_ident, get_rustdoc, parse_graphql_attrs, remove_graphql_attrs,
GeneratorResult, GeneratorResult,
}; };
use inflector::Inflector; use inflector::Inflector;
@ -428,18 +428,6 @@ pub fn generate(
} }
}); });
let post_guard = match &method_args.post_guard {
Some(meta_list) => generate_post_guards(&crate_name, meta_list)?,
None => None,
};
let post_guard = post_guard.map(|guard| {
quote! {
#guard.check(ctx, &res).await
.map_err(|err| err.into_server_error().at(ctx.item.pos))?;
}
});
resolvers.push(quote! { resolvers.push(quote! {
#(#cfg_attrs)* #(#cfg_attrs)*
if ctx.item.node.name.node == #field_name { if ctx.item.node.name.node == #field_name {
@ -447,7 +435,6 @@ pub fn generate(
#guard #guard
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
let res = #resolve_obj; let res = #resolve_obj;
#post_guard
return #crate_name::OutputValueType::resolve(&res, &ctx_obj, ctx.item).await.map(::std::option::Option::Some); return #crate_name::OutputValueType::resolve(&res, &ctx_obj, ctx.item).await.map(::std::option::Option::Some);
} }
}); });

View File

@ -1,7 +1,5 @@
use crate::args; use crate::args;
use crate::utils::{ use crate::utils::{generate_guards, get_crate_name, get_rustdoc, GeneratorResult};
generate_guards, generate_post_guards, get_crate_name, get_rustdoc, GeneratorResult,
};
use darling::ast::Data; use darling::ast::Data;
use inflector::Inflector; use inflector::Inflector;
use proc_macro::TokenStream; use proc_macro::TokenStream;
@ -102,12 +100,6 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
}; };
let guard = guard.map(|guard| quote! { #guard.check(ctx).await.map_err(|err| err.into_server_error().at(ctx.item.pos))?; }); let guard = guard.map(|guard| quote! { #guard.check(ctx).await.map_err(|err| err.into_server_error().at(ctx.item.pos))?; });
let post_guard = match &field.post_guard {
Some(meta) => generate_post_guards(&crate_name, &meta)?,
None => None,
};
let post_guard = post_guard.map(|guard| quote! { #guard.check(ctx, &res).await.map_err(|err| err.into_server_error().at(ctx.item.pos))?; });
getters.push(if !field.owned { getters.push(if !field.owned {
quote! { quote! {
#[inline] #[inline]
@ -131,7 +123,6 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
#guard #guard
let res = self.#ident(ctx).await.map_err(|err| err.into_server_error().at(ctx.item.pos))?; let res = self.#ident(ctx).await.map_err(|err| err.into_server_error().at(ctx.item.pos))?;
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
#post_guard
return #crate_name::OutputValueType::resolve(&res, &ctx_obj, ctx.item).await.map(::std::option::Option::Some); return #crate_name::OutputValueType::resolve(&res, &ctx_obj, ctx.item).await.map(::std::option::Option::Some);
} }
}); });

View File

@ -260,13 +260,6 @@ pub fn generate(
let guard = guard.map(|guard| quote! { let guard = guard.map(|guard| quote! {
#guard.check(ctx).await.map_err(|err| err.into_server_error().at(ctx.item.pos))?; #guard.check(ctx).await.map_err(|err| err.into_server_error().at(ctx.item.pos))?;
}); });
if field.post_guard.is_some() {
return Err(Error::new_spanned(
method,
"The subscription field does not support post guard",
)
.into());
}
let stream_fn = quote! { let stream_fn = quote! {
#(#get_params)* #(#get_params)*

View File

@ -122,165 +122,120 @@ pub fn generate_guards(
args: &Meta, args: &Meta,
) -> GeneratorResult<Option<TokenStream>> { ) -> GeneratorResult<Option<TokenStream>> {
match args { match args {
Meta::List(args) => { Meta::List(args) => match args.path.get_ident() {
match args.path.get_ident() { Some(ident) => match ident.to_string().as_str() {
Some(ident) => match ident.to_string().as_str() { "guard" => {
"guard" => { if args.nested.len() != 1 {
if args.nested.len() != 1 { return Err(Error::new_spanned(
return Err(Error::new_spanned( args,
args, "Chained rules isn't possible anymore, please use operators.",
"Chained rules isn't possible anymore, please use operators.", )
) .into());
.into());
}
if let NestedMeta::Meta(rule) = &args.nested[0] {
generate_guards(crate_name, rule)
} else {
Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into())
}
} }
"and" => { if let NestedMeta::Meta(rule) = &args.nested[0] {
if args.nested.len() != 2 { generate_guards(crate_name, rule)
return Err(Error::new_spanned( } else {
args, Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into())
"and operator support only 2 operands.",
)
.into());
}
let first_rule: Option<TokenStream>;
let second_rule: Option<TokenStream>;
if let NestedMeta::Meta(rule) = &args.nested[0] {
first_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into());
}
if let NestedMeta::Meta(rule) = &args.nested[1] {
second_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[1], "Invalid rule.").into());
}
Ok(Some(
quote! { #crate_name::guard::GuardExt::and(#first_rule, #second_rule) },
))
} }
"or" => { }
if args.nested.len() != 2 { "and" => {
return Err(Error::new_spanned( if args.nested.len() != 2 {
args, return Err(Error::new_spanned(
"or operator support only 2 operands.", args,
) "and operator support only 2 operands.",
.into()); )
} .into());
let first_rule: Option<TokenStream>;
let second_rule: Option<TokenStream>;
if let NestedMeta::Meta(rule) = &args.nested[0] {
first_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into());
}
if let NestedMeta::Meta(rule) = &args.nested[1] {
second_rule = generate_guards(crate_name, rule)?;
} else {
return Err(Error::new_spanned(&args.nested[1], "Invalid rule.").into());
}
Ok(Some(
quote! { #crate_name::guard::GuardExt::or(#first_rule, #second_rule) },
))
} }
"chain" => { let first_rule: Option<TokenStream>;
if args.nested.len() < 2 { let second_rule: Option<TokenStream>;
return Err(Error::new_spanned( if let NestedMeta::Meta(rule) = &args.nested[0] {
args, first_rule = generate_guards(crate_name, rule)?;
"chain operator need at least 1 operand.", } else {
) return Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into());
.into());
}
let mut guards: Option<TokenStream> = None;
for arg in &args.nested {
if let NestedMeta::Meta(rule) = &arg {
let guard = generate_guards(crate_name, rule)?;
if guards.is_none() {
guards = guard;
} else {
guards =
Some(quote! { #crate_name::guard::GuardExt::and(#guard, #guards) });
}
}
}
Ok(guards)
} }
"race" => { if let NestedMeta::Meta(rule) = &args.nested[1] {
if args.nested.len() < 2 { second_rule = generate_guards(crate_name, rule)?;
return Err(Error::new_spanned( } else {
args, return Err(Error::new_spanned(&args.nested[1], "Invalid rule.").into());
"race operator need at least 1 operand.",
)
.into());
}
let mut guards: Option<TokenStream> = None;
for arg in &args.nested {
if let NestedMeta::Meta(rule) = &arg {
let guard = generate_guards(crate_name, rule)?;
if guards.is_none() {
guards = guard;
} else {
guards =
Some(quote! { #crate_name::guard::GuardExt::or(#guard, #guards) });
}
}
}
Ok(guards)
} }
_ => { Ok(Some(
let ty = &args.path; quote! { #crate_name::guard::GuardExt::and(#first_rule, #second_rule) },
let mut params = Vec::new(); ))
for attr in &args.nested { }
if let NestedMeta::Meta(Meta::NameValue(nv)) = attr { "or" => {
let name = &nv.path; if args.nested.len() != 2 {
if let Lit::Str(value) = &nv.lit { return Err(Error::new_spanned(
let value_str = value.value(); args,
if value_str.starts_with('@') { "or operator support only 2 operands.",
let getter_name = get_param_getter_ident(&value_str[1..]); )
params.push(quote! { #name: #getter_name()? }); .into());
} else { }
let expr = syn::parse_str::<Expr>(&value_str)?; let first_rule: Option<TokenStream>;
params.push(quote! { #name: (#expr).into() }); let second_rule: Option<TokenStream>;
} if let NestedMeta::Meta(rule) = &args.nested[0] {
} else { first_rule = generate_guards(crate_name, rule)?;
return Err(Error::new_spanned( } else {
&nv.lit, return Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into());
"Value must be string literal", }
) if let NestedMeta::Meta(rule) = &args.nested[1] {
.into()); second_rule = generate_guards(crate_name, rule)?;
} } else {
return Err(Error::new_spanned(&args.nested[1], "Invalid rule.").into());
}
Ok(Some(
quote! { #crate_name::guard::GuardExt::or(#first_rule, #second_rule) },
))
}
"chain" => {
if args.nested.len() < 2 {
return Err(Error::new_spanned(
args,
"chain operator need at least 1 operand.",
)
.into());
}
let mut guards: Option<TokenStream> = None;
for arg in &args.nested {
if let NestedMeta::Meta(rule) = &arg {
let guard = generate_guards(crate_name, rule)?;
if guards.is_none() {
guards = guard;
} else { } else {
return Err( guards = Some(
Error::new_spanned(attr, "Invalid property for guard").into() quote! { #crate_name::guard::GuardExt::and(#guard, #guards) },
); );
} }
} }
Ok(Some(quote! { #ty { #(#params),* } }))
} }
}, Ok(guards)
None => Err(Error::new_spanned(args, "Invalid guards").into()), }
} "race" => {
} if args.nested.len() < 2 {
_ => Err(Error::new_spanned(args, "Invalid guards").into()), return Err(Error::new_spanned(
} args,
} "race operator need at least 1 operand.",
)
pub fn generate_post_guards( .into());
crate_name: &TokenStream, }
args: &Meta, let mut guards: Option<TokenStream> = None;
) -> GeneratorResult<Option<TokenStream>> { for arg in &args.nested {
match args { if let NestedMeta::Meta(rule) = &arg {
Meta::List(args) => { let guard = generate_guards(crate_name, rule)?;
let mut guards = None; if guards.is_none() {
for item in &args.nested { guards = guard;
if let NestedMeta::Meta(Meta::List(ls)) = item { } else {
let ty = &ls.path; guards = Some(
quote! { #crate_name::guard::GuardExt::or(#guard, #guards) },
);
}
}
}
Ok(guards)
}
_ => {
let ty = &args.path;
let mut params = Vec::new(); let mut params = Vec::new();
for attr in &ls.nested { for attr in &args.nested {
if let NestedMeta::Meta(Meta::NameValue(nv)) = attr { if let NestedMeta::Meta(Meta::NameValue(nv)) = attr {
let name = &nv.path; let name = &nv.path;
if let Lit::Str(value) = &nv.lit { if let Lit::Str(value) = &nv.lit {
@ -305,19 +260,11 @@ pub fn generate_post_guards(
); );
} }
} }
let guard = quote! { #ty { #(#params),* } }; Ok(Some(quote! { #ty { #(#params),* } }))
if guards.is_none() {
guards = Some(guard);
} else {
guards =
Some(quote! { #crate_name::guard::PostGuardExt::and(#guard, #guards) });
}
} else {
return Err(Error::new_spanned(item, "Invalid guard").into());
} }
} },
Ok(guards) None => Err(Error::new_spanned(args, "Invalid guards").into()),
} },
_ => Err(Error::new_spanned(args, "Invalid guards").into()), _ => Err(Error::new_spanned(args, "Invalid guards").into()),
} }
} }

View File

@ -205,7 +205,6 @@ pub use types::*;
/// | 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 |
/// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | /// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y |
/// | guard | Field of guard | [`Guard`](guard/trait.Guard.html) | Y | /// | guard | Field of guard | [`Guard`](guard/trait.Guard.html) | Y |
/// | post_guard | Field of post guard | [`PostGuard`](guard/trait.PostGuard.html) | Y |
/// ///
/// # Field argument parameters /// # Field argument parameters
/// ///
@ -321,7 +320,6 @@ pub use async_graphql_derive::Object;
/// | 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 |
/// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | /// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y |
/// | guard | Field of guard | [`Guard`](guard/trait.Guard.html) | Y | /// | guard | Field of guard | [`Guard`](guard/trait.Guard.html) | Y |
/// | post_guard | Field of post guard | [`PostGuard`](guard/trait.PostGuard.html) | Y |
/// ///
/// # Examples /// # Examples
/// ///
@ -678,7 +676,6 @@ pub use async_graphql_derive::Union;
/// | name | Field name | string | Y | /// | name | Field name | string | Y |
/// | deprecation | Field deprecation reason | string | Y | /// | deprecation | Field deprecation reason | string | Y |
/// | guard | Field of guard | [`Guard`](guard/trait.Guard.html) | Y | /// | guard | Field of guard | [`Guard`](guard/trait.Guard.html) | Y |
/// | post_guard | Field of post guard | [`PostGuard`](guard/trait.PostGuard.html) | Y |
/// ///
/// # Field argument parameters /// # Field argument parameters
/// ///

View File

@ -291,7 +291,11 @@ pub async fn test_guard_or_operator() {
pub async fn test_guard_chain_operator() { pub async fn test_guard_chain_operator() {
#[derive(SimpleObject)] #[derive(SimpleObject)]
struct Query { struct Query {
#[graphql(guard(chain(RoleGuard(role = "Role::Admin"), UserGuard(username = r#""test""#), AgeGuard(age = r#"21"#))))] #[graphql(guard(chain(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#),
AgeGuard(age = r#"21"#)
)))]
value: i32, value: i32,
} }
@ -396,7 +400,11 @@ pub async fn test_guard_chain_operator() {
pub async fn test_guard_race_operator() { pub async fn test_guard_race_operator() {
#[derive(SimpleObject)] #[derive(SimpleObject)]
struct Query { struct Query {
#[graphql(guard(race(RoleGuard(role = "Role::Admin"), UserGuard(username = r#""test""#), AgeGuard(age = r#"21"#))))] #[graphql(guard(race(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#),
AgeGuard(age = r#"21"#)
)))]
value: i32, value: i32,
} }
@ -477,4 +485,4 @@ pub async fn test_guard_race_operator() {
extensions: None, extensions: None,
}] }]
); );
} }

View File

@ -1,327 +0,0 @@
use async_graphql::guard::PostGuard;
use async_graphql::*;
#[derive(Eq, PartialEq, Copy, Clone)]
enum Role {
Admin,
Guest,
}
struct RoleGuard {
role: Role,
}
#[async_trait::async_trait]
impl PostGuard<i32> for RoleGuard {
async fn check(&self, ctx: &Context<'_>, _result: &i32) -> Result<()> {
if ctx.data_opt::<Role>() == Some(&self.role) {
Ok(())
} else {
Err("Forbidden".into())
}
}
}
#[derive(SimpleObject)]
struct MyObj {
#[graphql(owned, post_guard(UserGuard(username = r#""test""#, value = "88")))]
value: i32,
}
struct Username(String);
struct UserGuard {
value: i32,
username: String,
}
#[async_trait::async_trait]
impl PostGuard<i32> for UserGuard {
async fn check(&self, ctx: &Context<'_>, result: &i32) -> Result<()> {
assert_eq!(*result, self.value);
if ctx.data_opt::<Username>().as_ref().map(|s| s.0.as_str()) == Some(&self.username) {
Ok(())
} else {
Err("Forbidden".into())
}
}
}
#[async_trait::async_trait]
impl PostGuard<MyObj> for UserGuard {
async fn check(&self, ctx: &Context<'_>, result: &MyObj) -> Result<()> {
assert_eq!(result.value, self.value);
if ctx.data_opt::<Username>().as_ref().map(|s| s.0.as_str()) == Some(&self.username) {
Ok(())
} else {
Err("Forbidden".into())
}
}
}
#[async_std::test]
pub async fn test_post_guard() {
struct Query;
#[Object]
impl Query {
#[graphql(post_guard(UserGuard(username = r#""test""#, value = "99")))]
async fn value(&self) -> i32 {
99
}
async fn obj(&self) -> MyObj {
MyObj { value: 88 }
}
}
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let query = "{ value }";
assert_eq!(
schema
.execute(Request::new(query).data(Username("test".to_string())))
.await
.data,
serde_json::json!({
"value": 99
})
);
let query = "{ value }";
assert_eq!(
schema
.execute(Request::new(query).data(Username("test1".to_string())))
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: "Forbidden".to_string(),
locations: vec![Pos { line: 1, column: 3 }],
path: vec![PathSegment::Field("value".to_owned())],
extensions: None,
}]
);
let query = "{ obj { value } }";
assert_eq!(
schema
.execute(Request::new(query).data(Username("test".to_string())))
.await
.data,
serde_json::json!({
"obj": { "value": 88 }
})
);
let query = "{ obj { value } }";
assert_eq!(
schema
.execute(Request::new(query).data(Username("test1".to_string())))
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: "Forbidden".to_string(),
locations: vec![Pos { line: 1, column: 9 }],
path: vec![
PathSegment::Field("obj".to_owned()),
PathSegment::Field("value".to_owned())
],
extensions: None,
}]
);
}
#[async_std::test]
pub async fn test_multiple_post_guards() {
#[derive(SimpleObject)]
struct Query {
#[graphql(post_guard(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#, value = "10")
))]
value: i32,
}
let schema = Schema::new(Query { value: 10 }, EmptyMutation, EmptySubscription);
let query = "{ value }";
assert_eq!(
schema
.execute(
Request::new(query)
.data(Role::Admin)
.data(Username("test".to_string()))
)
.await
.data,
serde_json::json!({"value": 10})
);
let query = "{ value }";
assert_eq!(
schema
.execute(
Request::new(query)
.data(Role::Guest)
.data(Username("test".to_string()))
)
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: "Forbidden".to_string(),
locations: vec![Pos { line: 1, column: 3 }],
path: vec![PathSegment::Field("value".to_owned())],
extensions: None,
}]
);
let query = "{ value }";
assert_eq!(
schema
.execute(
Request::new(query)
.data(Role::Admin)
.data(Username("test1".to_string()))
)
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: "Forbidden".to_string(),
locations: vec![Pos { line: 1, column: 3 }],
path: vec![PathSegment::Field("value".to_owned())],
extensions: None,
}]
);
let query = "{ value }";
assert_eq!(
schema
.execute(
Request::new(query)
.data(Role::Guest)
.data(Username("test1".to_string()))
)
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: "Forbidden".to_string(),
locations: vec![Pos { line: 1, column: 3 }],
path: vec![PathSegment::Field("value".to_owned())],
extensions: None,
}]
);
}
#[async_std::test]
pub async fn test_post_guard_forward_arguments() {
struct UserGuard {
id: ID,
}
#[async_trait::async_trait]
impl PostGuard<ID> for UserGuard {
async fn check(&self, ctx: &Context<'_>, result: &ID) -> Result<()> {
assert_eq!(result.as_str(), "haha");
if ctx.data_opt::<ID>() != Some(&self.id) {
Err("Forbidden".into())
} else {
Ok(())
}
}
}
struct QueryRoot;
#[Object]
impl QueryRoot {
#[graphql(post_guard(UserGuard(id = "@_id")))]
async fn user(&self, _id: ID) -> ID {
"haha".into()
}
}
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
let query = r#"{ user(id: "abc") }"#;
assert_eq!(
schema
.execute(Request::new(query).data(ID::from("abc")))
.await
.data,
serde_json::json!({"user": "haha"})
);
let query = r#"{ user(id: "abc") }"#;
assert_eq!(
schema
.execute(Request::new(query).data(ID::from("aaa")))
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: "Forbidden".to_string(),
locations: vec![Pos { line: 1, column: 3 }],
path: vec![PathSegment::Field("user".to_owned())],
extensions: None,
}]
);
}
#[async_std::test]
pub async fn test_post_guard_generic() {
struct UserGuard {
id: ID,
}
#[async_trait::async_trait]
impl<T: Send + Sync> PostGuard<T> for UserGuard {
async fn check(&self, ctx: &Context<'_>, _result: &T) -> Result<()> {
if ctx.data_opt::<ID>() != Some(&self.id) {
Err("Forbidden".into())
} else {
Ok(())
}
}
}
struct QueryRoot;
#[Object]
impl QueryRoot {
#[graphql(post_guard(UserGuard(id = r#""abc""#)))]
async fn user(&self) -> ID {
"haha".into()
}
}
let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
let query = r#"{ user }"#;
assert_eq!(
schema
.execute(Request::new(query).data(ID::from("abc")))
.await
.data,
serde_json::json!({"user": "haha"})
);
let query = r#"{ user }"#;
assert_eq!(
schema
.execute(Request::new(query).data(ID::from("aaa")))
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: "Forbidden".to_string(),
locations: vec![Pos { line: 1, column: 3 }],
path: vec![PathSegment::Field("user".to_owned())],
extensions: None,
}]
);
}