Git Product home page Git Product logo

Comments (14)

juhaku avatar juhaku commented on May 20, 2024

Could you modify it to take them into account?

This seems like it is rocket specific feature if I'm not mistaken. It could be possible to add and can be done at some point :) But I am quite piled up with tasks at the moment so it wont be very top on the list unfortunately.

On the other hand, that #[form] does similar functionality that is already supported with serde attributes. Serde support is general to all frameworks and thus is preferred over the framework specific things.

Also, what's the best way to use this VecParam generic type in a field, so that I don't have to duplicate the whole FilterParams type just for the doc spec (which can go out of sync with the real type used in the handler)?
This is an important question because many of the API types have fields that use generics.

Not sure do I understand your question correctly. But could not the VecParam be just Vec<...>? Nor is not the FilterParams the type that is used as an argument to the fn my_handler(filter_params: FilterParams)? I suppose that FilterParams can be defined multiple times by adding it over multiple different functions.

params(
    ("filter_params" = FilterParams, description = "Filter parameters"),
)

Could you give an example of your your code like how you would use it or what is the case you are trying to implement if non of my nonsense helps 😂

from utoipa.

oliver-impero avatar oliver-impero commented on May 20, 2024

Thanks for your quick reply :)

But could not the VecParam be just Vec<...>?

Unfortunately not. This is a wrapper around Vec for the purpose of deserializing it from a comma-separated list (in a FromFormValue impl), it's not possible to use Vec here. Btw, this is just one example, there are many others were generics are used in field types.

Nor is not the FilterParams the type that is used as an argument to the fn my_handler(filter_params: FilterParams)?

The handler argument is filter_params: Form<ListControlResultsFilterParams> (using rocket's Form).

I suppose that FilterParams can be defined multiple times by adding it over multiple different functions.

How do you mean? :)
What I want to avoid is having to duplicate most types that occur in the API just because they are generics or have generics in their fields.

Could you give an example of your your code

It's not so easy because it's confidential and Github still doesn't support confidential issues (that only the repo owner can see).
But it's basically like the code I wrote above, the VecParam has to be used, and it is generic. And FilterParams has to be a form.

from utoipa.

oliver-impero avatar oliver-impero commented on May 20, 2024

@juhaku Btw, regarding my filter_params, it seems I can make it work by writing an isomorphic struct (using a monomorphized VecParamUuid) that is only used in the utoipa attributes.

Do I have to write a params entry for every field of FilterParams, like this?

    params(
        ("ids" = Option<VecParamUuid>, query, style = Form, description = "ids"),
        ("archived" = Option<bool>, query, style = Form, description = "archived"),
        ("dueDateStart" = Option<i64>, query, style = Form, description = "dueDateStart"),
        ("dueDateEnd" = Option<i64>, query, style = Form, description = "dueDateEnd"),
        ("periodStart" = Option<String>, query, style = Form, description = "periodStart"),
        ("periodEnd" = Option<String>, query, style = Form, description = "periodEnd"),
    )
)]
#[get("/results?<filter_params..>")]

Or can I write it like this?

    params(
        ("filter_params" = FilterParams, query, style = Form, description = "Filter parameters"),
    )
)]
#[get("/results?<filter_params..>")]

The VecParam fields need to be parsed like this, how can I specify that in params(..)? :)

https://swagger.io/docs/specification/serialization/
image


Another small question: Currently I'm doing Config::from("/api-doc/v1.json"), how can I pass additional config params that are not part of the API spec, such as "tryItOutEnabled": false, to disable the "Try it out" button for handlers?
https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/

from utoipa.

juhaku avatar juhaku commented on May 20, 2024

@oliver-impero Sorry for late reply, been really busy lately leaving me freetime between zero and none.

Do I have to write a params entry for every field of FilterParams, like this?

This was actually the first ever plan I had in my mind what comes to parameters. And that is a valid way to describe the parameters. And to be honest it never crossed my mind that parameters could be described like this. 😂

#[derive(Serliaze, Deserialize, Component, Form)]
struct Params {
    foo: Option<String>
}
//... 

#[utoipa::path(
    params(
        ("params"  = Params, description = "Query Params")
    )
)]
fn foo(params: Form<Params>) {...}

If that works thats brilliant though currently it indeed lacks a way to override the type and other configs for arguments in this form.

However how I intended the params to be used is either like

params(
       ("ids" = Option<VecParamUuid>, query, style = Form, description = "ids"),
       ("archived" = Option<bool>, query, style = Form, description = "archived"),
       ("dueDateStart" = Option<i64>, query, style = Form, description = "dueDateStart"),
       ("dueDateEnd" = Option<i64>, query, style = Form, description = "dueDateEnd"),
       ("periodStart" = Option<String>, query, style = Form, description = "periodStart"),
       ("periodEnd" = Option<String>, query, style = Form, description = "periodEnd"),
   )

This approach is works fine with every framework. But also the <VecParamUuid> currently cannot give correct type for the argument. If the VecParamUuid is unnamed struct struct VecParamUuid(Vec<Uuid>); then the utoipa is able to tell that the type is actually Vec<Uuid> and not VecParamUuid. If the VecParamUuid is named struct like seen below Utoipa is not able to tell that the type represented in OpenAPI should be Vec<Uuid>. e.g.

struct VecParamUuid {
    ids: Vec<Uuid>,
}

However the params(...) section is only for declaring what's shown in the OpenAPI spec. So you could write the this way as well:

// .. derives omitted
struct MyActualParams {
    ids: Option<VecParamUuid>,
}
//...
  params(
    ("ids" = Option<[Uuid]>, query, style = Form, description = "ids"),
  )
// ...
fn foo(params: From<MyActualParmas>) {...}

And it would work since the entries defined within params(....here...) does not actually care about the type itself and does not make any type checks against the actual struct or object. It only uses the given type semantics of params and creates the OpenAPI operation parameters based on that. See the docs https://docs.rs/utoipa/1.0.2/utoipa/attr.path.html#params-attributes for more details but in short the type in parameter entry can be in a format of:

  • ids = [Uuid] making it Vec/slice of Uuid's
  • ids = Option<[Uuid]> making it optional Vec/slice of Uuid's
  • ids = Uuid making it Uuid
  • ids = Option<Uuid> making it optional Uuid

But what comes to defining parameters with structs my end goal was to eventually have similar approach with IntoParams trait that exists for actix-web framework. Example: https://docs.rs/utoipa/1.0.2/utoipa/derive.IntoParams.html And currently you can define the style and other arguments for parameters Only within IntoParams implementation or directly within entries of params(...) section. But not in Component as they had different purpose actually.

The style = Form attribute controls how parameters are shown. You may find more information in the specification here: https://swagger.io/specification/#parameter-object just browse done a little to the table of Style Values. The default style is controlled by the in attribute where parameter is placed. And default to the array types (slice / vec) is Form.

To get it represented as comma separated list you need to define style as Simple ("ids" = Option<[Uuid]>, query, style = Simple, description = "ids"),

And when it is possible to use IntoParmas or similar with rocket the idea that I had was ofcourse allow as much as code sharing as possible so that one could write something like this.

#[derive(Serialize, Deserialize, IntoParams)]
struct MyParams {
...
}

#[utoipa::path(...)]
fn foo(my_params: Path<MyParams>) {...}

#[utoipa::path(...)]
fn bar(my_params: Path<MyParams>) {...}

In above code the both of the endpoints would share same parameters which are also the actual parameters for the function and not duplicate parameter objects.

from utoipa.

juhaku avatar juhaku commented on May 20, 2024

Another small question: Currently I'm doing Config::from("/api-doc/v1.json"), how can I pass additional config params that are not part of the API spec, such as "tryItOutEnabled": false, to disable the "Try it out" button for handlers?
https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/

At the moment it is not possible to to alter Swagger UI configurations aside of controlling the name, url or primary fields of api doc url(s). It's in my plans to have it there but so far other features have been the priority since most of people are fine with the defaults. Just recently the configuration parameters where improved to support the oauth parameters by contributor. But eventually, yes, more granual control over Swagger UI configuration will be supported. :)

from utoipa.

oliver-impero avatar oliver-impero commented on May 20, 2024

Hm, this doesn't seem to work, it still expects me to define filter_params even though I defined all constituents:

    params(
        ("ids" = Option<VecParamUuid>, query, style = Form, description = "ids"),
        ("archived" = Option<bool>, query, style = Form, description = "archived"),
        ("dueDateStart" = Option<i64>, query, style = Form, description = "dueDateStart"),
        ("dueDateEnd" = Option<i64>, query, style = Form, description = "dueDateEnd"),
        ("periodStart" = Option<String>, query, style = Form, description = "periodStart"),
        ("periodEnd" = Option<String>, query, style = Form, description = "periodEnd"),
    )
)]
#[get("/foo?<filter_params..>")]
pub fn foo(
    executor: ApiKeyExecutor,
    config: State<AppConfig>,
    page_query: PaginationQuery,
    filter_params: LenientForm<FilterParams>,
)

When trying to generate the API client, I get this error:

Exception in thread "main" org.openapitools.codegen.SpecValidationException: There were issues with the specification. The option can be disabled via validateSpec (Maven/Gradle) or --skip-validate-spec (CLI).
 | Error count: 1, Warning count: 0
Errors:
        -attribute paths.'/public-api/v1/foo'(get).parameters.[filter_params].schemas.#/components/schemas/FilterParams is missing

        at org.openapitools.codegen.config.CodegenConfigurator.toContext(CodegenConfigurator.java:556)
        at org.openapitools.codegen.config.CodegenConfigurator.toClientOptInput(CodegenConfigurator.java:583)
        at org.openapitools.codegen.cmd.Generate.execute(Generate.java:433)
        at org.openapitools.codegen.cmd.OpenApiGeneratorCommand.run(OpenApiGeneratorCommand.java:32)
        at org.openapitools.codegen.OpenAPIGenerator.main(OpenAPIGenerator.java:66)

and in the browser:

Could not resolve reference: Could not resolve pointer: /components/schemas/FilterParams does not exist in document

How to make this work / how to specify in the attribute that it should ignore filter_params?
Thanks! :)


Btw, in the generated doc, it doesn't seem to make it apparent when a param is optional, how can it be made visible that a param is optional (Option)?

from utoipa.

juhaku avatar juhaku commented on May 20, 2024

How to make this work / how to specify in the attribute that it should ignore filter_params?
Thanks! :)

Oh, this is actually not possible with the current implementation. Quote from the docs below 👇

Utoipa is only able to parse parameter types for primitive types, String, Vec, Option or std::path::PathBuf type. Other function arguments are simply ignored.

And the <filter_params..> wont be ignored since it is defined in the #[get(...)] path macro. And this causes the issue you encounter. It will add the reference to FilterParams as demonstrated with following code:

#[get("/foo?<filter_params..>")]
pub fn foo(
   executor: ApiKeyExecutor,
   config: State<AppConfig>,
   page_query: PaginationQuery,
   filter_params: LenientForm<FilterParams>, <-- This will add the reference to FilterParams.
)

And since FilterParams is not #[derive(Component)] and is not added to the OpenApi with #[openapi(components(FilterParams))] thus not finding it from the generated OpenAPI as well. And really it should not be component though but it should not create reference either. Although it creates it now becuase the the #[get(...<filter_params..>)] macro. This is something that needs special handling and some throughout thinking of possible solutions to get around this issue with rocket.

Btw, in the generated doc, it doesn't seem to make it apparent when a param is optional, how can it be made visible that a param is optional (Option)?

Hmm that's weird, the Option<...> should make the parameter in the doc as optional. If the type is just the concrete type there will appear red * infront and wont let users to try out the endpoint without filling the argument. When it is Option<...> users can optionally fill the field in the Swagger UI.

from utoipa.

oliver-impero avatar oliver-impero commented on May 20, 2024

So the only workaround is to list each filter param manually in the url string and handler args?

from utoipa.

juhaku avatar juhaku commented on May 20, 2024

Yes, for time being it is unfortunately. This is something that need to be investigated and see whether it is possible and how much effort it would take to support the form style parameters.

from utoipa.

JSH32 avatar JSH32 commented on May 20, 2024

Could there be a way to support lists without wrapping it in a generic + aliases?
Something like this (status = 200, body = Vec<ApplicationData>),

from utoipa.

juhaku avatar juhaku commented on May 20, 2024

Yes they are supported if I understand your question right. For example the lists can be defined like so
(status = 200, body = [ApplicationData]). Lists are wrapped in brackets. https://docs.rs/utoipa/1.1.0/utoipa/attr.path.html#responses-attributes

from utoipa.

juhaku avatar juhaku commented on May 20, 2024

@oliver-impero Update about this feature, see #222, I'm now working on this and the support should land in next 2.0.0 release :) Of course using master branch you will get it immediately when it's merged. 🙂

from utoipa.

juhaku avatar juhaku commented on May 20, 2024

Closing due inactivity.

from utoipa.

juhaku avatar juhaku commented on May 20, 2024

@JSH32

Something like this (status = 200, body = Vec),

With #408 The body can also support Vec<...> types as well. This is because the parsing logic will be the same as used in ToSchema and IntoParams.

from utoipa.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.