Add or operator

This commit is contained in:
Aurelien Foucault 2020-10-03 23:00:34 +02:00
parent 49811a47cc
commit bca3489776
3 changed files with 167 additions and 11 deletions

View File

@ -156,6 +156,24 @@ pub fn generate_guards(
}
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<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) }))
}
_ => {
let ty = &args.path;
let mut params = Vec::new();

View File

@ -16,10 +16,15 @@ pub trait Guard {
/// An extension trait for `Guard`.
pub trait GuardExt: Guard + Sized {
/// Merge the two guards.
/// Perform `and` operator on two rules
fn and<R: Guard>(self, other: R) -> And<Self, R> {
And(self, other)
}
/// Perform `or` operator on two rules
fn or<R: Guard>(self, other: R) -> Or<Self, R> {
Or(self, other)
}
}
impl<T: Guard> GuardExt for T {}
@ -30,8 +35,17 @@ pub struct And<A: Guard, B: Guard>(A, B);
#[async_trait::async_trait]
impl<A: Guard + Send + Sync, B: Guard + Send + Sync> Guard for And<A, B> {
async fn check(&self, ctx: &Context<'_>) -> Result<()> {
self.0.check(ctx).await?;
self.1.check(ctx).await
self.0.check(ctx).await.and(self.1.check(ctx).await)
}
}
/// Guard for [`GuardExt::or`](trait.GuardExt.html#method.or).
pub struct Or<A: Guard, B: Guard>(A, B);
#[async_trait::async_trait]
impl<A: Guard + Send + Sync, B: Guard + Send + Sync> Guard for Or<A, B> {
async fn check(&self, ctx: &Context<'_>) -> Result<()> {
self.0.check(ctx).await.or(self.1.check(ctx).await)
}
}

View File

@ -41,7 +41,49 @@ impl Guard for UserGuard {
}
#[async_std::test]
pub async fn test_multiple_guards() {
pub async fn test_guard_simple_rule() {
#[derive(SimpleObject)]
struct Query {
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
value: i32,
}
let schema = Schema::new(Query { value: 10 }, EmptyMutation, EmptySubscription);
let query = "{ value }";
assert_eq!(
schema
.execute(
Request::new(query)
.data(Role::Admin)
)
.await
.data,
serde_json::json!({"value": 10})
);
let query = "{ value }";
assert_eq!(
schema
.execute(
Request::new(query)
.data(Role::Guest)
)
.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_guard_and_operator() {
#[derive(SimpleObject)]
struct Query {
@ -49,13 +91,7 @@ pub async fn test_multiple_guards() {
value: i32,
}
#[derive(SimpleObject)]
struct Mutation {
value_m: i32,
}
let schema = Schema::new(Query { value: 10 }, Mutation {valueM: 11}, EmptySubscription);
let schema = Schema::new(Query { value: 10 }, EmptyMutation, EmptySubscription);
let query = "{ value }";
assert_eq!(
@ -108,4 +144,92 @@ pub async fn test_multiple_guards() {
}]
);
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_guard_or_operator() {
#[derive(SimpleObject)]
struct Query {
#[graphql(guard(or(RoleGuard(role = "Role::Admin"), UserGuard(username = r#""test""#))))]
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
.data,
serde_json::json!({"value": 10})
);
let query = "{ value }";
assert_eq!(
schema
.execute(
Request::new(query)
.data(Role::Admin)
.data(Username("test1".to_string()))
)
.await
.data,
serde_json::json!({"value": 10})
);
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,
}]
);
}