GREsau / okapi

OpenAPI (AKA Swagger) document generation for Rust projects
MIT License
634 stars 112 forks source link

how do I set a default value for a parameter? #70

Closed TaeyoonKwon closed 2 years ago

TaeyoonKwon commented 2 years ago

I am quite new to rust and found that default parameter is not allowed in rust.

but I could not find a way to set a default value for a parameter in the swagger documentation page.

How can I set the default value for like page = 1 in such codes?

#[openapi(tag = "Customer")]
#[get("/customer?<limit>&<page>")]
pub async fn get_customers(
    db: &State<Database>,
    limit: i64,
    page: Option<i64>,
) -> Result<Json<Vec<Customer>>, Json<MessageResponse>> 
ralpha commented 2 years ago

Yes you can. Just add something like this.

pub async fn get_customers(
    db: &State<Database>,
    limit: i64,
    page: Option<i64>,
) -> Result<Json<Vec<Customer>>, Json<MessageResponse>> {
    println!("Page before: {:?}", page);
    let page: i64 = page.unwrap_or(1);
    println!("Page after: {:?}", page);
}

For more info: https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap_or Here you can see it in action: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cbb0649856d78080ac86ea857ac6e2dc

This is how unwrap_or() is implemented internally: https://doc.rust-lang.org/src/core/option.rs.html#766 (match is like switch case in a lot of other languages)

You can also do: page.unwrap_or_default() but the default for i64 is 0. As defined by the Default trait for i64.

If you have more questions, just ask. (if not, you can close the issue)

TaeyoonKwon commented 2 years ago

Perfect! Thank you so much.

TaeyoonKwon commented 2 years ago

Oh, I am sorry that I was not clear, but what I was looking for is to explicitly put the default value at swagger-ui like this.

image

ralpha commented 2 years ago

Hmm... not sure this is (easily) possible at this point. Because this is right in between Schemars and Okapi. And also right on the edge of Rust's easy to create macro flexibility.

I think you would have to create a new struct in that case. Because this is a query or path parameter (and not the request or response body) it is not easy to add metadata to the types.

You could try:

fn default_page_query_param() -> PageQueryParami64 {
    PageQueryParami64(1)
}

#[derive(Deserialize, Serialize, JsonSchema, Debug)]
#[serde(default = "default_page_query_param")]
struct PageQueryParami64(i64);

impl Default for PageQueryParami64 {
    fn default() -> Self{
        default_page_query_param()
    }
}

pub async fn get_customers(
    db: &State<Database>,
    limit: i64,
    page: PageQueryParami64,
) -> Result<Json<Vec<Customer>>, Json<MessageResponse>> {
    println!("Page before: {:?}", page);
    let page: i64 = page.0;
    println!("Page after: {:?}", page);
}

Source: https://github.com/GREsau/schemars/issues/6 and https://serde.rs/field-attrs.html (I have not tested code above, so could contain errors, but gives you an idea) The first function in combination with #[serde(default = "default_page_query_param")] is what adds the default value to the openapi.json file. The impl Default for PageQueryParami64 { is what sets the value to 1 if no value was set. But I'm not sure if it now marks the value as requires in the docs. (if so use page: Option<PageQueryParami64>, and let page: i64 = page.unwrap_or_default().0;)

There are also "example values" (not the same as default) might might also fill them in, not sure. For more info see: https://github.com/GREsau/okapi/issues/11 But approach is about the same as above at this point.

But this is already a bit more involved. Let me know if you got stuck somewhere while trying this.

ralpha commented 2 years ago

No further response, closing issue.