Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject dependency into a filter using openapi macro #102

Open
seguidor777 opened this issue Dec 1, 2021 · 6 comments
Open

Inject dependency into a filter using openapi macro #102

seguidor777 opened this issue Dec 1, 2021 · 6 comments

Comments

@seguidor777
Copy link

seguidor777 commented Dec 1, 2021

Hello,

I'm using the openapi feature and trying to pass the database service to the filter, is this possible?
I need to implement a custom filter to compare the header content against the database records.

This is the code I have:

fn authenticate(db_service: DbService,) -> impl Clone + Filter<Extract = (), Error = Rejection> {
    let token = rweb::header::<String>("authorization");

    db_service.verify_token(&token);
    // TODO: Return filter
}
#[get("/products")]
#[openapi(id = "products.get")]
#[openapi(summary = "List products")]
#[openapi(response(code = "200", description = "OK"))]
pub fn list_products(
    #[data] db_service: DbService,
    #[filter = "authenticate(db_service)"] _header: (),
) -> Json<Vec<Product>> {
    Json::from(db_service.list_products().unwrap())
}

When I run this code it returns the following error:

error: custom attribute panicked
  --> src/api/products.rs:33:1
   |
33 | #[get("/products")]
   | ^^^^^^^^^^^^^^^^^^
   |
   = help: message: unexpected token
@seguidor777
Copy link
Author

After digging into the source code a bit, I found that the filters don't take any parameters.
I think I should first add a filter to extract the header from the token, then I would need to call the authenticate function from the handler function. Something like this

fn token_header() -> impl Clone + Filter<Extract = (String,), Error = Rejection> {
    rweb::header::<String>("authorization")
}

#[get("/products")]
#[openapi(id = "products.get")]
#[openapi(summary = "List products")]
#[openapi(response(code = "200", description = "OK"))]
#[openapi(response(code = "401", description = "Unauthorized"))]
pub fn list_products(
    #[data] db_service: DbService,
    #[filter = "token_header"] token: String,
) -> Json<Vec<Product>> {
    **// Call to authenticate(db_service, token)**
    Json::from(db_service.list_products().unwrap())
}

However, I would like to call the authenticate method from a filter and be able to reuse it in the other handlers.
I think it's important to be able to add filters using with as in this example seanmonstar/warp#62 (comment)

@kdy1
Copy link
Owner

kdy1 commented Dec 1, 2021

See

rweb/tests/router.rs

Lines 48 to 49 in 624d66e

#[get("/use")]
fn use_db(#[data] _db: Db) -> String {

@kdy1 kdy1 closed this as completed Dec 1, 2021
@E-gy
Copy link
Contributor

E-gy commented Dec 1, 2021

@kdy1 afaiu The question is about using #[data] in a filter, or rather in general passing [custom] arguments to filters.

// This is a filter that takes a custom argument
fn authenticate(db_service: DbService,) -> impl Clone + Filter<Extract = (), Error = Rejection> {
    let token = rweb::header::<String>("authorization");

    db_service.verify_token(&token);
    // TODO: Return filter
}

#[get("/products")]
pub fn list_products(
    #[data] db_service: DbService,
    // Here we (attempt to) use the `authenticate` filter, with the `db_service` argument received from data
    #[filter = "authenticate(db_service)"] _header: (),
) -> Json<Vec<Product>> {
    Json::from(db_service.list_products().unwrap())
}

@kdy1 kdy1 reopened this Dec 1, 2021
@seguidor777
Copy link
Author

seguidor777 commented Dec 1, 2021

In fact, I am looking for filters where we can inject any kind of dependencies and do some middleware tasks like logging, tracing, authentication and authorization. I know this is possible with the normal handlers, but I have not seen how to do it through the openapi macros

In this tutorial there is an example of a filter which receives three dependencies, I'm trying to achieve the same with openapi
https://github.com/zupzup/rust-casbin-example/blob/main/src/main.rs#L53

@szagi3891
Copy link
Contributor

What if you build a function that returns a function ? That is, you could potentially make a function that receives dependencies and returns a function.

But the problem will remain, because it will not be possible to inject this filter using a macro :/

@seguidor777
Copy link
Author

Yea and I think using the macro is essential because makes the code more ergonomic

@seguidor777 seguidor777 changed the title How to inject a dependency into a filter Inject dependency into a filter using openapi macro Dec 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants