diff --git a/derive/src/args.rs b/derive/src/args.rs index c0bcf094..3c6b88b5 100644 --- a/derive/src/args.rs +++ b/derive/src/args.rs @@ -69,8 +69,6 @@ pub struct SimpleObjectField { pub requires: Option, #[darling(default)] pub guard: Option, - #[darling(default)] - pub post_guard: Option, } #[derive(FromDeriveInput)] @@ -123,7 +121,6 @@ pub struct ObjectField { pub provides: Option, pub requires: Option, pub guard: Option, - pub post_guard: Option, } #[derive(FromDeriveInput)] @@ -302,7 +299,6 @@ pub struct SubscriptionField { pub name: Option, pub deprecation: Option, pub guard: Option, - pub post_guard: Option, } #[derive(FromMeta, Default)] diff --git a/derive/src/object.rs b/derive/src/object.rs index df950b9a..d884c5dd 100644 --- a/derive/src/object.rs +++ b/derive/src/object.rs @@ -1,8 +1,8 @@ use crate::args; use crate::output_type::OutputType; use crate::utils::{ - generate_default, generate_guards, generate_post_guards, generate_validator, get_cfg_attrs, - get_crate_name, get_param_getter_ident, get_rustdoc, parse_graphql_attrs, remove_graphql_attrs, + generate_default, generate_guards, generate_validator, get_cfg_attrs, get_crate_name, + get_param_getter_ident, get_rustdoc, parse_graphql_attrs, remove_graphql_attrs, GeneratorResult, }; 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! { #(#cfg_attrs)* if ctx.item.node.name.node == #field_name { @@ -447,7 +435,6 @@ pub fn generate( #guard let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); let res = #resolve_obj; - #post_guard return #crate_name::OutputValueType::resolve(&res, &ctx_obj, ctx.item).await.map(::std::option::Option::Some); } }); diff --git a/derive/src/simple_object.rs b/derive/src/simple_object.rs index ccae27d6..af1cb54c 100644 --- a/derive/src/simple_object.rs +++ b/derive/src/simple_object.rs @@ -1,7 +1,5 @@ use crate::args; -use crate::utils::{ - generate_guards, generate_post_guards, get_crate_name, get_rustdoc, GeneratorResult, -}; +use crate::utils::{generate_guards, get_crate_name, get_rustdoc, GeneratorResult}; use darling::ast::Data; use inflector::Inflector; use proc_macro::TokenStream; @@ -102,12 +100,6 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult 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 { quote! { #[inline] @@ -131,7 +123,6 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult GeneratorResult> { match args { - Meta::List(args) => { - match args.path.get_ident() { - Some(ident) => match ident.to_string().as_str() { - "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()) - } + Meta::List(args) => match args.path.get_ident() { + Some(ident) => match ident.to_string().as_str() { + "guard" => { + if args.nested.len() != 1 { + return Err(Error::new_spanned( + args, + "Chained rules isn't possible anymore, please use operators.", + ) + .into()); } - "and" => { - if args.nested.len() != 2 { - return Err(Error::new_spanned( - args, - "and operator support only 2 operands.", - ) - .into()); - } - let first_rule: Option; - let second_rule: Option; - 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) }, - )) + if let NestedMeta::Meta(rule) = &args.nested[0] { + generate_guards(crate_name, rule) + } else { + Err(Error::new_spanned(&args.nested[0], "Invalid rule.").into()) } - "or" => { - if args.nested.len() != 2 { - return Err(Error::new_spanned( - args, - "or operator support only 2 operands.", - ) - .into()); - } - let first_rule: Option; - let second_rule: Option; - 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) }, - )) + } + "and" => { + if args.nested.len() != 2 { + return Err(Error::new_spanned( + args, + "and operator support only 2 operands.", + ) + .into()); } - "chain" => { - if args.nested.len() < 2 { - return Err(Error::new_spanned( - args, - "chain operator need at least 1 operand.", - ) - .into()); - } - let mut guards: Option = 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) + let first_rule: Option; + let second_rule: Option; + 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()); } - "race" => { - if args.nested.len() < 2 { - return Err(Error::new_spanned( - args, - "race operator need at least 1 operand.", - ) - .into()); - } - let mut guards: Option = 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) + 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()); } - _ => { - 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 value_str.starts_with('@') { - let getter_name = get_param_getter_ident(&value_str[1..]); - params.push(quote! { #name: #getter_name()? }); - } else { - let expr = syn::parse_str::(&value_str)?; - params.push(quote! { #name: (#expr).into() }); - } - } else { - return Err(Error::new_spanned( - &nv.lit, - "Value must be string literal", - ) - .into()); - } + Ok(Some( + quote! { #crate_name::guard::GuardExt::and(#first_rule, #second_rule) }, + )) + } + "or" => { + if args.nested.len() != 2 { + return Err(Error::new_spanned( + args, + "or operator support only 2 operands.", + ) + .into()); + } + let first_rule: Option; + let second_rule: Option; + 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" => { + if args.nested.len() < 2 { + return Err(Error::new_spanned( + args, + "chain operator need at least 1 operand.", + ) + .into()); + } + let mut guards: Option = 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 { - return Err( - Error::new_spanned(attr, "Invalid property for guard").into() + guards = Some( + quote! { #crate_name::guard::GuardExt::and(#guard, #guards) }, ); } } - Ok(Some(quote! { #ty { #(#params),* } })) } - }, - None => Err(Error::new_spanned(args, "Invalid guards").into()), - } - } - _ => Err(Error::new_spanned(args, "Invalid guards").into()), - } -} - -pub fn generate_post_guards( - crate_name: &TokenStream, - args: &Meta, -) -> GeneratorResult> { - match args { - Meta::List(args) => { - let mut guards = None; - for item in &args.nested { - if let NestedMeta::Meta(Meta::List(ls)) = item { - let ty = &ls.path; + Ok(guards) + } + "race" => { + if args.nested.len() < 2 { + return Err(Error::new_spanned( + args, + "race operator need at least 1 operand.", + ) + .into()); + } + let mut guards: Option = 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) + } + _ => { + let ty = &args.path; let mut params = Vec::new(); - for attr in &ls.nested { + for attr in &args.nested { if let NestedMeta::Meta(Meta::NameValue(nv)) = attr { let name = &nv.path; if let Lit::Str(value) = &nv.lit { @@ -305,19 +260,11 @@ pub fn generate_post_guards( ); } } - let guard = 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(Some(quote! { #ty { #(#params),* } })) } - } - Ok(guards) - } + }, + None => Err(Error::new_spanned(args, "Invalid guards").into()), + }, _ => Err(Error::new_spanned(args, "Invalid guards").into()), } } diff --git a/src/lib.rs b/src/lib.rs index 55dfdddc..20a71f7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 | /// | 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 | -/// | post_guard | Field of post guard | [`PostGuard`](guard/trait.PostGuard.html) | Y | /// /// # 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 | /// | 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 | -/// | post_guard | Field of post guard | [`PostGuard`](guard/trait.PostGuard.html) | Y | /// /// # Examples /// @@ -678,7 +676,6 @@ pub use async_graphql_derive::Union; /// | name | Field name | string | Y | /// | deprecation | Field deprecation reason | string | 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 /// diff --git a/tests/guard.rs b/tests/guard.rs index 3b958498..377cc0ca 100644 --- a/tests/guard.rs +++ b/tests/guard.rs @@ -291,7 +291,11 @@ pub async fn test_guard_or_operator() { 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"#))))] + #[graphql(guard(chain( + RoleGuard(role = "Role::Admin"), + UserGuard(username = r#""test""#), + AgeGuard(age = r#"21"#) + )))] value: i32, } @@ -396,7 +400,11 @@ pub async fn test_guard_chain_operator() { 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"#))))] + #[graphql(guard(race( + RoleGuard(role = "Role::Admin"), + UserGuard(username = r#""test""#), + AgeGuard(age = r#"21"#) + )))] value: i32, } @@ -477,4 +485,4 @@ pub async fn test_guard_race_operator() { extensions: None, }] ); -} \ No newline at end of file +} diff --git a/tests/post_guard.rs b/tests/post_guard.rs deleted file mode 100644 index 55913b7c..00000000 --- a/tests/post_guard.rs +++ /dev/null @@ -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 for RoleGuard { - async fn check(&self, ctx: &Context<'_>, _result: &i32) -> Result<()> { - if ctx.data_opt::() == 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 for UserGuard { - async fn check(&self, ctx: &Context<'_>, result: &i32) -> Result<()> { - assert_eq!(*result, self.value); - if ctx.data_opt::().as_ref().map(|s| s.0.as_str()) == Some(&self.username) { - Ok(()) - } else { - Err("Forbidden".into()) - } - } -} - -#[async_trait::async_trait] -impl PostGuard for UserGuard { - async fn check(&self, ctx: &Context<'_>, result: &MyObj) -> Result<()> { - assert_eq!(result.value, self.value); - if ctx.data_opt::().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 for UserGuard { - async fn check(&self, ctx: &Context<'_>, result: &ID) -> Result<()> { - assert_eq!(result.as_str(), "haha"); - if ctx.data_opt::() != 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 PostGuard for UserGuard { - async fn check(&self, ctx: &Context<'_>, _result: &T) -> Result<()> { - if ctx.data_opt::() != 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, - }] - ); -}