Support guard simple rule with the rework (recursivity)

This commit is contained in:
Aurelien Foucault 2020-10-03 15:16:18 +02:00
parent 3581008e71
commit 216b363ce7
2 changed files with 81 additions and 253 deletions

View File

@ -121,52 +121,87 @@ pub fn generate_guards(
crate_name: &TokenStream,
args: &Meta,
) -> GeneratorResult<Option<TokenStream>> {
println!("{:#?}\n", args);
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;
let mut params = Vec::new();
for attr in &ls.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::<Expr>(&value_str)?;
params.push(quote! { #name: (#expr).into() });
}
} else {
return Err(Error::new_spanned(
&nv.lit,
"Value must be string literal",
)
.into());
println!("args = {:#?}\n", args.path);
match args.path.get_ident() {
Some(ident) => {
match ident.to_string().as_str() {
"guard" => {
println!("ident guard found {:#?}\n", ident);
if args.nested.len() > 1 {
return Err(Error::new_spanned(args, "Chained rules isn't possible anymore, please use operators.").into());
}
} else {
return Err(
Error::new_spanned(attr, "Invalid property for guard").into()
);
// why moved ? Want to use it in error
match &args.nested[0] {
NestedMeta::Meta(rule) => {
println! ("rule sended = {:#?}\n", rule);
return generate_guards(crate_name, rule);
}
_ => {
return Err(Error::new_spanned(args, "Invalid guard (to be improve)").into());
}
}
}
"and" => {
println!("ident and found {:#?}\n", ident);
return Err(Error::new_spanned(args, "WIP").into());
}
_ => {
let mut guards = None;
//args == "guard"
println!("items = {:#?}\n", &args.nested);
let ty = &args.path;
//ty = the rule
println!("ty = {:#?}\n", ty);
let mut params = Vec::new();
//rules params
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::<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()
);
}
}
let guard = quote! { #ty { #(#params),* } };
if guards.is_none() {
guards = Some(guard);
} else {
guards =
Some(quote! { #crate_name::guard::GuardExt::and(#guard, #guards) });
}
println!("end of function");
Ok(guards)
//return Err(Error::new_spanned(ident, "Invalid guard (to be improve 2)").into());
}
}
let guard = quote! { #ty { #(#params),* } };
if guards.is_none() {
guards = Some(guard);
} else {
guards =
Some(quote! { #crate_name::guard::GuardExt::and(#guard, #guards) });
}
} else {
return Err(Error::new_spanned(item, "Invalid guard").into());
},
None => {
println!("failed for the moment");
return Err(Error::new_spanned(args, "WIP").into());
}
}
Ok(guards)
}
_ => Err(Error::new_spanned(args, "Invalid guards").into()),
_ => Err(Error::new_spanned(args, "Invalid guards (old)").into()),
}
}

View File

@ -40,137 +40,21 @@ impl Guard for UserGuard {
}
}
#[async_std::test]
pub async fn test_guard() {
#[derive(SimpleObject)]
struct MyObj {
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
value: i32,
}
struct Query;
#[Object]
impl Query {
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
async fn value(&self) -> i32 {
1
}
async fn obj(&self) -> MyObj {
MyObj { value: 99 }
}
}
struct Subscription;
#[Subscription]
impl Subscription {
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
async fn values(&self) -> impl Stream<Item = i32> {
futures::stream::iter(vec![1, 2, 3])
}
}
let schema = Schema::new(Query, EmptyMutation, Subscription);
let query = "{ obj { value } }";
assert_eq!(
schema
.execute(Request::new(query).data(Role::Admin))
.await
.data,
serde_json::json!({
"obj": {"value": 99}
})
);
let query = "{ obj { value } }";
assert_eq!(
schema
.execute(Request::new(query).data(Role::Guest))
.await
.into_result()
.unwrap_err(),
vec![ServerError {
message: "Forbidden".to_owned(),
locations: vec![Pos { line: 1, column: 9 }],
path: vec![
PathSegment::Field("obj".to_owned()),
PathSegment::Field("value".to_owned())
],
extensions: None,
}]
);
let query = "{ value }";
assert_eq!(
schema
.execute(Request::new(query).data(Role::Admin))
.await
.data,
serde_json::json!({
"value": 1,
})
);
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,
}]
);
assert_eq!(
schema
.execute_stream(Request::new("subscription { values }").data(Role::Admin))
.map(|item| item.data)
.collect::<Vec<_>>()
.await,
vec![
serde_json::json! ({"values": 1}),
serde_json::json! ({"values": 2}),
serde_json::json! ({"values": 3})
]
);
assert_eq!(
schema
.execute_stream(Request::new("subscription { values }").data(Role::Guest))
.boxed()
.next()
.await
.unwrap()
.errors,
vec![ServerError {
message: "Forbidden".to_string(),
locations: vec![Pos {
line: 1,
column: 16
}],
path: vec![PathSegment::Field("values".to_owned())],
extensions: None,
}]
);
}
#[async_std::test]
pub async fn test_multiple_guards() {
#[derive(SimpleObject)]
struct Query {
#[graphql(guard(RoleGuard(role = "Role::Admin"), UserGuard(username = r#""test""#)))]
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
value: i32,
}
let schema = Schema::new(Query { value: 10 }, EmptyMutation, EmptySubscription);
#[derive(SimpleObject)]
struct Mutation {
value: i32,
}
let schema = Schema::new(Query { value: 10 }, Mutation {value: 11}, EmptySubscription);
let query = "{ value }";
assert_eq!(
@ -204,95 +88,4 @@ pub async fn test_multiple_guards() {
}]
);
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_guard_forward_arguments() {
struct UserGuard {
id: ID,
}
#[async_trait::async_trait]
impl Guard for UserGuard {
async fn check(&self, ctx: &Context<'_>) -> Result<()> {
if ctx.data_opt::<ID>() != Some(&self.id) {
Err("Forbidden".into())
} else {
Ok(())
}
}
}
struct QueryRoot;
#[Object]
impl QueryRoot {
#[graphql(guard(UserGuard(id = "@id")))]
async fn user(&self, id: ID) -> ID {
id
}
}
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": "abc"})
);
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,
}]
);
}