Rework guards
This commit is contained in:
parent
28f027a8bc
commit
60245769f5
|
@ -1,5 +1,5 @@
|
|||
use darling::ast::{Data, Fields};
|
||||
use darling::util::Ignored;
|
||||
use darling::util::{Ignored, SpannedValue};
|
||||
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant};
|
||||
use inflector::Inflector;
|
||||
use syn::{
|
||||
|
@ -142,7 +142,7 @@ pub struct SimpleObjectField {
|
|||
#[darling(default)]
|
||||
pub requires: Option<String>,
|
||||
#[darling(default)]
|
||||
pub guard: Option<Meta>,
|
||||
pub guard: Option<SpannedValue<String>>,
|
||||
#[darling(default)]
|
||||
pub visible: Option<Visible>,
|
||||
#[darling(default, multiple)]
|
||||
|
@ -258,7 +258,7 @@ pub struct ObjectField {
|
|||
pub external: bool,
|
||||
pub provides: Option<String>,
|
||||
pub requires: Option<String>,
|
||||
pub guard: Option<Meta>,
|
||||
pub guard: Option<SpannedValue<String>>,
|
||||
pub visible: Option<Visible>,
|
||||
pub complexity: Option<ComplexityType>,
|
||||
#[darling(default, multiple)]
|
||||
|
@ -495,7 +495,7 @@ pub struct SubscriptionField {
|
|||
pub skip: bool,
|
||||
pub name: Option<String>,
|
||||
pub deprecation: Deprecation,
|
||||
pub guard: Option<Meta>,
|
||||
pub guard: Option<SpannedValue<String>>,
|
||||
pub visible: Option<Visible>,
|
||||
pub complexity: Option<ComplexityType>,
|
||||
}
|
||||
|
@ -693,7 +693,7 @@ pub struct ComplexObjectField {
|
|||
pub external: bool,
|
||||
pub provides: Option<String>,
|
||||
pub requires: Option<String>,
|
||||
pub guard: Option<Meta>,
|
||||
pub guard: Option<SpannedValue<String>>,
|
||||
pub visible: Option<Visible>,
|
||||
pub complexity: Option<ComplexityType>,
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ use crate::args::{self, ComplexityType, RenameRuleExt, RenameTarget};
|
|||
use crate::output_type::OutputType;
|
||||
use crate::utils::{
|
||||
extract_input_args, gen_deprecation, generate_default, generate_guards, get_cfg_attrs,
|
||||
get_crate_name, get_param_getter_ident, get_rustdoc, get_type_path_and_name,
|
||||
parse_complexity_expr, parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
|
||||
get_crate_name, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
|
||||
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
|
||||
};
|
||||
|
||||
pub fn generate(
|
||||
|
@ -234,12 +234,9 @@ pub fn generate(
|
|||
Some(quote!(.map_err(|err| err.into_server_error(__pos)))),
|
||||
)?;
|
||||
|
||||
let param_getter_name = get_param_getter_ident(&ident.ident.unraw().to_string());
|
||||
get_params.push(quote! {
|
||||
#[allow(non_snake_case)]
|
||||
let #param_getter_name = || { ctx.param_value::<#ty>(#name, #default) };
|
||||
#[allow(non_snake_case)]
|
||||
let (__pos, #ident) = #param_getter_name()?;
|
||||
let (__pos, #ident) = ctx.param_value::<#ty>(#name, #default)?;
|
||||
#validators
|
||||
});
|
||||
}
|
||||
|
@ -343,8 +340,11 @@ pub fn generate(
|
|||
}
|
||||
};
|
||||
|
||||
let guard_map_err = quote! {
|
||||
.map_err(|err| err.into_server_error(ctx.item.pos))
|
||||
};
|
||||
let guard = match &method_args.guard {
|
||||
Some(meta_list) => generate_guards(&crate_name, meta_list)?,
|
||||
Some(code) => Some(generate_guards(&crate_name, code, guard_map_err)?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ use crate::args::{self, ComplexityType, RenameRuleExt, RenameTarget};
|
|||
use crate::output_type::OutputType;
|
||||
use crate::utils::{
|
||||
extract_input_args, gen_deprecation, generate_default, generate_guards, get_cfg_attrs,
|
||||
get_crate_name, get_param_getter_ident, get_rustdoc, get_type_path_and_name,
|
||||
parse_complexity_expr, parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
|
||||
get_crate_name, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
|
||||
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
|
||||
};
|
||||
|
||||
pub fn generate(
|
||||
|
@ -369,13 +369,9 @@ pub fn generate(
|
|||
Some(quote!(.map_err(|err| err.into_server_error(__pos)))),
|
||||
)?;
|
||||
|
||||
let param_getter_name =
|
||||
get_param_getter_ident(&ident.ident.unraw().to_string());
|
||||
get_params.push(quote! {
|
||||
#[allow(non_snake_case)]
|
||||
let #param_getter_name = || ctx.param_value::<#ty>(#name, #default);
|
||||
#[allow(non_snake_case, unused_variables)]
|
||||
let (__pos, #ident) = #param_getter_name()?;
|
||||
let (__pos, #ident) = ctx.param_value::<#ty>(#name, #default)?;
|
||||
#validators
|
||||
});
|
||||
}
|
||||
|
@ -479,17 +475,14 @@ pub fn generate(
|
|||
}
|
||||
};
|
||||
|
||||
let guard_map_err = quote! {
|
||||
.map_err(|err| err.into_server_error(ctx.item.pos))
|
||||
};
|
||||
let guard = match &method_args.guard {
|
||||
Some(meta_list) => generate_guards(&crate_name, meta_list)?,
|
||||
Some(code) => Some(generate_guards(&crate_name, code, guard_map_err)?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let guard = guard.map(|guard| {
|
||||
quote! {
|
||||
#guard.check(ctx).await.map_err(|err| err.into_server_error(ctx.item.pos))?;
|
||||
}
|
||||
});
|
||||
|
||||
resolvers.push(quote! {
|
||||
#(#cfg_attrs)*
|
||||
if ctx.item.node.name.node == #field_name {
|
||||
|
|
|
@ -168,13 +168,13 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
|||
});
|
||||
});
|
||||
|
||||
let guard_map_err = quote! {
|
||||
.map_err(|err| err.into_server_error(ctx.item.pos))
|
||||
};
|
||||
let guard = match &field.guard {
|
||||
Some(meta) => generate_guards(&crate_name, meta)?,
|
||||
Some(code) => Some(generate_guards(&crate_name, code, guard_map_err)?),
|
||||
None => None,
|
||||
};
|
||||
let guard = guard.map(
|
||||
|guard| quote! { #guard.check(ctx).await.map_err(|err| err.into_server_error(ctx.item.pos))?; },
|
||||
);
|
||||
|
||||
let with_function = derived.as_ref().and_then(|x| x.with.as_ref());
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ use syn::{
|
|||
use crate::args::{self, ComplexityType, RenameRuleExt, RenameTarget, SubscriptionField};
|
||||
use crate::output_type::OutputType;
|
||||
use crate::utils::{
|
||||
gen_deprecation, generate_default, generate_guards, get_cfg_attrs, get_crate_name,
|
||||
get_param_getter_ident, get_rustdoc, get_type_path_and_name, parse_complexity_expr,
|
||||
parse_graphql_attrs, remove_graphql_attrs, visible_fn, GeneratorResult,
|
||||
gen_deprecation, generate_default, generate_guards, get_cfg_attrs, get_crate_name, get_rustdoc,
|
||||
get_type_path_and_name, parse_complexity_expr, parse_graphql_attrs, remove_graphql_attrs,
|
||||
visible_fn, GeneratorResult,
|
||||
};
|
||||
|
||||
pub fn generate(
|
||||
|
@ -203,12 +203,9 @@ pub fn generate(
|
|||
Some(quote!(.map_err(|err| err.into_server_error(__pos)))),
|
||||
)?;
|
||||
|
||||
let param_getter_name = get_param_getter_ident(&ident.ident.unraw().to_string());
|
||||
get_params.push(quote! {
|
||||
#[allow(non_snake_case)]
|
||||
let #param_getter_name = || { ctx.param_value::<#ty>(#name, #default) };
|
||||
#[allow(non_snake_case)]
|
||||
let (__pos, #ident) = #param_getter_name()?;
|
||||
let (__pos, #ident) = ctx.param_value::<#ty>(#name, #default)?;
|
||||
#validators
|
||||
});
|
||||
}
|
||||
|
@ -322,16 +319,16 @@ pub fn generate(
|
|||
})
|
||||
};
|
||||
|
||||
let guard = match &field.guard {
|
||||
Some(meta_list) => generate_guards(&crate_name, meta_list)?,
|
||||
None => None,
|
||||
};
|
||||
let guard = guard.map(|guard| quote! {
|
||||
#guard.check(ctx).await.map_err(|err| {
|
||||
let guard_map_err = quote! {
|
||||
.map_err(|err| {
|
||||
err.into_server_error(ctx.item.pos)
|
||||
.with_path(::std::vec![#crate_name::PathSegment::Field(::std::borrow::ToOwned::to_owned(&*field_name))])
|
||||
})?;
|
||||
});
|
||||
})
|
||||
};
|
||||
let guard = match &field.guard {
|
||||
Some(code) => Some(generate_guards(&crate_name, code, guard_map_err)?),
|
||||
None => None,
|
||||
};
|
||||
let stream_fn = quote! {
|
||||
let field_name = ::std::clone::Clone::clone(&ctx.item.node.response_key().node);
|
||||
let field = ::std::sync::Arc::new(::std::clone::Clone::clone(&ctx.item));
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use darling::util::SpannedValue;
|
||||
use darling::FromMeta;
|
||||
use proc_macro2::{Span, TokenStream, TokenTree};
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use quote::quote;
|
||||
use syn::visit::Visit;
|
||||
use syn::{
|
||||
Attribute, Error, Expr, ExprPath, FnArg, Ident, ImplItemMethod, Lit, LitStr, Meta, NestedMeta,
|
||||
Pat, PatIdent, Type, TypeGroup, TypeParamBound, TypeReference,
|
||||
Attribute, Error, Expr, ExprPath, FnArg, Ident, ImplItemMethod, Lit, LitStr, Meta, Pat,
|
||||
PatIdent, Type, TypeGroup, TypeParamBound, TypeReference,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::args;
|
||||
use crate::args::{Argument, Deprecation, Visible};
|
||||
use crate::args::{self, Argument, Deprecation, Visible};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GeneratorError {
|
||||
|
@ -48,143 +48,18 @@ pub fn get_crate_name(internal: bool) -> TokenStream {
|
|||
|
||||
pub fn generate_guards(
|
||||
crate_name: &TokenStream,
|
||||
args: &Meta,
|
||||
) -> GeneratorResult<Option<TokenStream>> {
|
||||
match args {
|
||||
Meta::List(args) => match args.path.get_ident().map(ToString::to_string) {
|
||||
Some(ident) if ident == "guard" => {
|
||||
if args.nested.len() != 1 {
|
||||
return Err(Error::new_spanned(
|
||||
args,
|
||||
"Chained rules isn't possible anymore, please use operators.",
|
||||
)
|
||||
.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())
|
||||
}
|
||||
}
|
||||
Some(ident) if ident == "and" => {
|
||||
if args.nested.len() != 2 {
|
||||
return Err(
|
||||
Error::new_spanned(args, "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) },
|
||||
))
|
||||
}
|
||||
Some(ident) if ident == "or" => {
|
||||
if args.nested.len() != 2 {
|
||||
return Err(
|
||||
Error::new_spanned(args, "or 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::or(#first_rule, #second_rule) },
|
||||
))
|
||||
}
|
||||
Some(ident) if ident == "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 {
|
||||
guards =
|
||||
Some(quote! { #crate_name::guard::GuardExt::and(#guards, #guard) });
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(guards)
|
||||
}
|
||||
Some(ident) if ident == "race" => {
|
||||
if args.nested.len() < 2 {
|
||||
return Err(
|
||||
Error::new_spanned(args, "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(#guards, #guard) });
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(guards)
|
||||
}
|
||||
_ => {
|
||||
let ty = &args.path;
|
||||
let mut params = Vec::new();
|
||||
for attr in &args.nested {
|
||||
if let NestedMeta::Meta(Meta::NameValue(nv)) = attr {
|
||||
let name = &nv.path;
|
||||
if let Lit::Str(value) = &nv.lit {
|
||||
let value_str = value.value();
|
||||
if let Some(value_str) = value_str.strip_prefix('@') {
|
||||
let getter_name = get_param_getter_ident(value_str);
|
||||
params.push(
|
||||
quote! { #name: #getter_name().map(|(_, value)| value)? },
|
||||
);
|
||||
} else {
|
||||
let expr = syn::parse_str::<Expr>(&value_str)?;
|
||||
params.push(quote! { #name: (#expr).into() });
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new_spanned(
|
||||
&nv.lit,
|
||||
"Value must be string literal",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new_spanned(attr, "Invalid property for guard").into());
|
||||
}
|
||||
}
|
||||
Ok(Some(quote! { #ty { #(#params),* } }))
|
||||
}
|
||||
},
|
||||
_ => Err(Error::new_spanned(args, "Invalid guards").into()),
|
||||
}
|
||||
code: &SpannedValue<String>,
|
||||
map_err: TokenStream,
|
||||
) -> GeneratorResult<TokenStream> {
|
||||
let expr: Expr =
|
||||
syn::parse_str(code).map_err(|err| Error::new(code.span(), err.to_string()))?;
|
||||
let code = quote! {{
|
||||
use #crate_name::guard::GuardExt;
|
||||
#expr
|
||||
}};
|
||||
Ok(quote! {
|
||||
#crate_name::guard::Guard::check(&#code, &ctx).await #map_err ?;
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_rustdoc(attrs: &[Attribute]) -> GeneratorResult<Option<String>> {
|
||||
|
@ -258,10 +133,6 @@ pub fn generate_default(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_param_getter_ident(name: &str) -> Ident {
|
||||
Ident::new(&format!("__{}_getter", name), Span::call_site())
|
||||
}
|
||||
|
||||
pub fn get_cfg_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
|
||||
attrs
|
||||
.iter()
|
||||
|
|
|
@ -2,7 +2,7 @@ use darling::util::SpannedValue;
|
|||
use darling::FromMeta;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{Expr, Lit, Result};
|
||||
use syn::{Error, Expr, Lit, Result};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Number {
|
||||
|
@ -111,7 +111,8 @@ impl Validators {
|
|||
}
|
||||
|
||||
for s in &self.custom {
|
||||
let expr: Expr = syn::parse_str(s)?;
|
||||
let expr: Expr =
|
||||
syn::parse_str(s).map_err(|err| Error::new(s.span(), err.to_string()))?;
|
||||
codes.push(quote! {
|
||||
#crate_name::CustomValidator::check(&(#expr), &ctx, #value).await
|
||||
.map_err(|err_msg| #crate_name::InputValueError::<#ty>::custom(err_msg))
|
||||
|
|
|
@ -5,8 +5,6 @@ use crate::{Context, Result};
|
|||
/// Field guard
|
||||
///
|
||||
/// Guard is a pre-condition for a field that is resolved if `Ok(())` is returned, otherwise an error is returned.
|
||||
///
|
||||
/// This trait is defined through the [`async-trait`](https://crates.io/crates/async-trait) macro.
|
||||
#[async_trait::async_trait]
|
||||
pub trait Guard {
|
||||
/// Check whether the guard will allow access to the field.
|
||||
|
|
255
tests/guard.rs
255
tests/guard.rs
|
@ -12,8 +12,10 @@ pub struct RoleGuard {
|
|||
role: Role,
|
||||
}
|
||||
|
||||
mod guards {
|
||||
pub use super::RoleGuard;
|
||||
impl RoleGuard {
|
||||
fn new(role: Role) -> Self {
|
||||
Self { role }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -29,31 +31,20 @@ impl Guard for RoleGuard {
|
|||
|
||||
struct Username(String);
|
||||
|
||||
struct UserGuard {
|
||||
username: String,
|
||||
struct UserGuard<'a> {
|
||||
username: &'a str,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Guard for UserGuard {
|
||||
async fn check(&self, ctx: &Context<'_>) -> Result<()> {
|
||||
if ctx.data_opt::<Username>().map(|name| &name.0).as_deref() == Some(&self.username) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Forbidden".into())
|
||||
}
|
||||
impl<'a> UserGuard<'a> {
|
||||
fn new(username: &'a str) -> Self {
|
||||
Self { username }
|
||||
}
|
||||
}
|
||||
|
||||
struct Age(i32);
|
||||
|
||||
struct AgeGuard {
|
||||
age: i32,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Guard for AgeGuard {
|
||||
impl<'a> Guard for UserGuard<'a> {
|
||||
async fn check(&self, ctx: &Context<'_>) -> Result<()> {
|
||||
if ctx.data_opt::<Age>().map(|name| &name.0) == Some(&self.age) {
|
||||
if ctx.data_opt::<Username>().map(|name| name.0.as_str()) == Some(self.username) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Forbidden".into())
|
||||
|
@ -65,7 +56,7 @@ impl Guard for AgeGuard {
|
|||
pub async fn test_guard_simple_rule() {
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
#[graphql(guard(guards::RoleGuard(role = "Role::Admin")))]
|
||||
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
|
||||
value: i32,
|
||||
}
|
||||
|
||||
|
@ -73,7 +64,7 @@ pub async fn test_guard_simple_rule() {
|
|||
|
||||
#[Subscription]
|
||||
impl Subscription {
|
||||
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
|
||||
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
|
||||
async fn values(&self) -> impl Stream<Item = i32> {
|
||||
futures_util::stream::iter(vec![1, 2, 3])
|
||||
}
|
||||
|
@ -143,10 +134,7 @@ pub async fn test_guard_simple_rule() {
|
|||
pub async fn test_guard_and_operator() {
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
#[graphql(guard(and(
|
||||
RoleGuard(role = "Role::Admin"),
|
||||
UserGuard(username = r#""test""#)
|
||||
)))]
|
||||
#[graphql(guard = r#"RoleGuard::new(Role::Admin).and(UserGuard::new("test"))"#)]
|
||||
value: i32,
|
||||
}
|
||||
|
||||
|
@ -230,7 +218,7 @@ pub async fn test_guard_and_operator() {
|
|||
pub async fn test_guard_or_operator() {
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
#[graphql(guard(or(RoleGuard(role = "Role::Admin"), UserGuard(username = r#""test""#))))]
|
||||
#[graphql(guard = r#"RoleGuard::new(Role::Admin).or(UserGuard::new("test"))"#)]
|
||||
value: i32,
|
||||
}
|
||||
|
||||
|
@ -296,211 +284,6 @@ pub async fn test_guard_or_operator() {
|
|||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn test_guard_chain_operator() {
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
#[graphql(guard(chain(
|
||||
RoleGuard(role = "Role::Admin"),
|
||||
UserGuard(username = r#""test""#),
|
||||
AgeGuard(age = r#"21"#)
|
||||
)))]
|
||||
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()))
|
||||
.data(Age(21))
|
||||
)
|
||||
.await
|
||||
.data,
|
||||
value!({"value": 10})
|
||||
);
|
||||
|
||||
let query = "{ value }";
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(
|
||||
Request::new(query)
|
||||
.data(Role::Guest)
|
||||
.data(Username("test".to_string()))
|
||||
.data(Age(21))
|
||||
)
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap_err(),
|
||||
vec![ServerError {
|
||||
message: "Forbidden".to_string(),
|
||||
source: None,
|
||||
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()))
|
||||
.data(Age(21))
|
||||
)
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap_err(),
|
||||
vec![ServerError {
|
||||
message: "Forbidden".to_string(),
|
||||
source: None,
|
||||
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("test".to_string()))
|
||||
.data(Age(22))
|
||||
)
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap_err(),
|
||||
vec![ServerError {
|
||||
message: "Forbidden".to_string(),
|
||||
source: None,
|
||||
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()))
|
||||
.data(Age(22))
|
||||
)
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap_err(),
|
||||
vec![ServerError {
|
||||
message: "Forbidden".to_string(),
|
||||
source: None,
|
||||
locations: vec![Pos { line: 1, column: 3 }],
|
||||
path: vec![PathSegment::Field("value".to_owned())],
|
||||
extensions: None,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn test_guard_race_operator() {
|
||||
#[derive(SimpleObject)]
|
||||
struct Query {
|
||||
#[graphql(guard(race(
|
||||
RoleGuard(role = "Role::Admin"),
|
||||
UserGuard(username = r#""test""#),
|
||||
AgeGuard(age = r#"21"#)
|
||||
)))]
|
||||
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()))
|
||||
.data(Age(21))
|
||||
)
|
||||
.await
|
||||
.data,
|
||||
value!({"value": 10})
|
||||
);
|
||||
|
||||
let query = "{ value }";
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(
|
||||
Request::new(query)
|
||||
.data(Role::Guest)
|
||||
.data(Username("test".to_string()))
|
||||
.data(Age(22))
|
||||
)
|
||||
.await
|
||||
.data,
|
||||
value!({"value": 10})
|
||||
);
|
||||
|
||||
let query = "{ value }";
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(
|
||||
Request::new(query)
|
||||
.data(Role::Admin)
|
||||
.data(Username("test1".to_string()))
|
||||
.data(Age(22))
|
||||
)
|
||||
.await
|
||||
.data,
|
||||
value!({"value": 10})
|
||||
);
|
||||
|
||||
let query = "{ value }";
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(
|
||||
Request::new(query)
|
||||
.data(Role::Guest)
|
||||
.data(Username("test1".to_string()))
|
||||
.data(Age(21))
|
||||
)
|
||||
.await
|
||||
.data,
|
||||
value!({"value": 10})
|
||||
);
|
||||
|
||||
let query = "{ value }";
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute(
|
||||
Request::new(query)
|
||||
.data(Role::Guest)
|
||||
.data(Username("test1".to_string()))
|
||||
.data(Age(22))
|
||||
)
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap_err(),
|
||||
vec![ServerError {
|
||||
message: "Forbidden".to_string(),
|
||||
source: None,
|
||||
locations: vec![Pos { line: 1, column: 3 }],
|
||||
path: vec![PathSegment::Field("value".to_owned())],
|
||||
extensions: None,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn test_guard_use_params() {
|
||||
struct EqGuard {
|
||||
|
@ -508,6 +291,12 @@ pub async fn test_guard_use_params() {
|
|||
actual: i32,
|
||||
}
|
||||
|
||||
impl EqGuard {
|
||||
fn new(expect: i32, actual: i32) -> Self {
|
||||
Self { expect, actual }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Guard for EqGuard {
|
||||
async fn check(&self, _ctx: &Context<'_>) -> Result<()> {
|
||||
|
@ -523,7 +312,7 @@ pub async fn test_guard_use_params() {
|
|||
|
||||
#[Object]
|
||||
impl Query {
|
||||
#[graphql(guard(EqGuard(expect = "100", actual = "@value")))]
|
||||
#[graphql(guard = "EqGuard::new(100, value)")]
|
||||
async fn get(&self, value: i32) -> i32 {
|
||||
value
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user