Git Product home page Git Product logo

utoipa's Introduction

utoipa - Auto-generated OpenAPI documentation

Utoipa build crates.io docs.rs MSRV

Pronounced /ju:ˈtoʊ:i.pɑ/ or /ju:ˈtoʊˌaɪ.piˈeɪ/ whatever works better for you.

Want to have your API documented with OpenAPI? But don't want to be bothered with manual YAML or JSON tweaking? Would like it to be so easy that it would almost be utopic? Don't worry: utoipa is here to fill this gap. It aims to do, if not all, then most of the heavy lifting for you, enabling you to focus on writing the actual API logic instead of documentation. It aims to be minimal, simple and fast. It uses simple proc macros which you can use to annotate your code to have items documented.

The utoipa crate provides auto-generated OpenAPI documentation for Rust REST APIs. It treats code-first approach as a first class citizen and simplifies API documentation by providing simple macros for generating the documentation from your code.

It also contains Rust types of the OpenAPI spec, allowing you to write the OpenAPI spec only using Rust if auto generation is not your flavor or does not fit your purpose.

Long term goal of the library is to be the place to go when OpenAPI documentation is needed in any Rust codebase.

Utoipa is framework-agnostic, and could be used together with any web framework, or even without one. While being portable and standalone, one of its key aspects is simple integration with web frameworks.

Choose your flavor and document your API with ice-cold IPA

Refer to the existing examples for building the "todo" app in the following frameworks:

All examples include a Swagger-UI unless stated otherwise.

There are also examples of building multiple OpenAPI docs in one application, each separated in Swagger UI. These examples exist only for the actix and warp frameworks.

Even if there is no example for your favourite framework, utoipa can be used with any web framework which supports decorating functions with macros similarly to the warp and tide examples.

Community examples

What's up with the word play?

The name comes from the words utopic and api where uto are the first three letters of utopic and the ipa is api reversed. Aaand... ipa is also an awesome type of beer 🍺.

Crate Features

  • yaml: Enables serde_yaml serialization of OpenAPI objects.
  • actix_extras: Enhances actix-web integration with being able to parse path, path and query parameters from actix web path attribute macros. See docs or examples for more details.
  • rocket_extras: Enhances rocket framework integration with being able to parse path, path and query parameters from rocket path attribute macros. See docs or examples for more details.
  • axum_extras: Enhances axum framework integration allowing users to use IntoParams without defining the parameter_in attribute. See docs or examples for more details.
  • debug: Add extra traits such as debug traits to openapi definitions and elsewhere.
  • chrono: Add support for chrono DateTime, Date, NaiveDate, NaiveDateTime, NaiveTime and Duration types. By default these types are parsed to string types with additional format information. format: date-time for DateTime and NaiveDateTime and format: date for Date and NaiveDate according RFC3339 as ISO-8601. To override default string representation users have to use value_type attribute to override the type. See docs for more details.
  • time: Add support for time OffsetDateTime, PrimitiveDateTime, Date, and Duration types. By default these types are parsed as string. OffsetDateTime and PrimitiveDateTime will use date-time format. Date will use date format and Duration will not have any format. To override default string representation users have to use value_type attribute to override the type. See docs for more details.
  • decimal: Add support for rust_decimal Decimal type. By default it is interpreted as String. If you wish to change the format you need to override the type. See the value_type in component derive docs.
  • decimal_float: Add support for rust_decimal Decimal type. By default it is interpreted as Number. This feature is mutually exclusive with decimal and allow to change the default type used in your documentation for Decimal much like serde_with_float feature exposed by rust_decimal.
  • uuid: Add support for uuid. Uuid type will be presented as String with format uuid in OpenAPI spec.
  • ulid: Add support for ulid. Ulid type will be presented as String with format ulid in OpenAPI spec.
  • url: Add support for url. Url type will be presented as String with format uri in OpenAPI spec.
  • smallvec: Add support for smallvec. SmallVec will be treated as Vec.
  • openapi_extensions: Adds traits and functions that provide extra convenience functions. See the request_body docs for an example.
  • repr: Add support for repr_serde's repr(u*) and repr(i*) attributes to unit type enums for C-like enum representation. See docs for more details.
  • preserve_order: Preserve order of properties when serializing the schema for a component. When enabled, the properties are listed in order of fields in the corresponding struct definition. When disabled, the properties are listed in alphabetical order.
  • preserve_path_order: Preserve order of OpenAPI Paths according to order they have been introduced to the #[openapi(paths(...))] macro attribute. If disabled the paths will be ordered in alphabetical order.
  • indexmap: Add support for indexmap. When enabled IndexMap will be rendered as a map similar to BTreeMap and HashMap.
  • non_strict_integers: Add support for non-standard integer formats int8, int16, uint8, uint16, uint32, and uint64.
  • rc_schema: Add ToSchema support for Arc<T> and Rc<T> types. Note! serde rc feature flag must be enabled separately to allow serialization and deserialization of Arc<T> and Rc<T> types. See more about serde feature flags.

Utoipa implicitly has partial support for serde attributes. See docs for more details.

Install

Add minimal dependency declaration to Cargo.toml.

[dependencies]
utoipa = "4"

To enable more features such as use actix framework extras you could define the dependency as follows.

[dependencies]
utoipa = { version = "4", features = ["actix_extras"] }

Note! To use utoipa together with Swagger UI you can use the utoipa-swagger-ui crate.

Examples

Create a struct, or it could also be an enum. Add ToSchema derive macro to it, so it can be registered as an OpenAPI schema.

use utoipa::ToSchema;

#[derive(ToSchema)]
struct Pet {
   id: u64,
   name: String,
   age: Option<i32>,
}

Create a handler that would handle your business logic and add path proc attribute macro over it.

mod pet_api {
    /// Get pet by id
    ///
    /// Get pet from database by pet id
    #[utoipa::path(
        get,
        path = "/pets/{id}",
        responses(
            (status = 200, description = "Pet found successfully", body = Pet),
            (status = NOT_FOUND, description = "Pet was not found")
        ),
        params(
            ("id" = u64, Path, description = "Pet database id to get Pet for"),
        )
    )]
    async fn get_pet_by_id(pet_id: u64) -> Pet {
        Pet {
            id: pet_id,
            age: None,
            name: "lightning".to_string(),
        }
    }
}

Utoipa has support for http StatusCode in responses.

This attribute macro will create another struct named with __path_ prefix + handler function name. So when you implement some_handler function in different file and want to export this, make sure __path_some_handler in the module can also be accessible from the root.

Tie the Schema and the endpoint above to the OpenAPI schema with following OpenApi derive proc macro.

use utoipa::OpenApi;

#[derive(OpenApi)]
#[openapi(paths(pet_api::get_pet_by_id), components(schemas(Pet)))]
struct ApiDoc;

println!("{}", ApiDoc::openapi().to_pretty_json().unwrap());

This would produce an API doc something similar to:

{
  "openapi": "3.0.3",
  "info": {
    "title": "application name from Cargo.toml",
    "description": "description from Cargo.toml",
    "contact": {
      "name": "author name from Cargo.toml",
      "email": "author email from Cargo.toml"
    },
    "license": {
      "name": "license from Cargo.toml"
    },
    "version": "version from Cargo.toml"
  },
  "paths": {
    "/pets/{id}": {
      "get": {
        "tags": ["pet_api"],
        "summary": "Get pet by id",
        "description": "Get pet by id\n\nGet pet from database by pet id\n",
        "operationId": "get_pet_by_id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Pet database id to get Pet for",
            "required": true,
            "deprecated": false,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 0.0,
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pet found successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pet"
                }
              }
            }
          },
          "404": {
            "description": "Pet was not found"
          }
        },
        "deprecated": false
      }
    }
  },
  "components": {
    "schemas": {
      "Pet": {
        "type": "object",
        "required": ["id", "name"],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64",
            "minimum": 0.0,
          },
          "name": {
            "type": "string"
          },
          "age": {
            "type": "integer",
            "format": "int32",
            "nullable": true,
          }
        }
      }
    }
  }
}

Modify OpenAPI at runtime

You can modify generated OpenAPI at runtime either via generated types directly or using Modify trait.

Modify generated OpenAPI via types directly.

#[derive(OpenApi)]
#[openapi(
    info(description = "My Api description"),
)]
struct ApiDoc;

let mut doc = ApiDoc::openapi();
doc.info.title = String::from("My Api");

You can even convert the generated OpenApi to OpenApiBuilder.

let builder: OpenApiBuilder = ApiDoc::openapi().into();

See Modify trait for examples on how to modify generated OpenAPI via it.

Go beyond the surface

FAQ

Swagger UI returns 404 NotFound from built binary

This is highly probably due to RustEmbed not embedding the Swagger UI to the executable. This is natural since the RustEmbed library does not by default embed files on debug builds. To get around this you can do one of the following.

  1. Build your executable in --release mode
  2. or add debug-embed feature flag to your Cargo.toml for utoipa-swagger-ui. This will enable the debug-emebed feature flag for RustEmbed as well. Read more about this here and here.

Find utoipa-swagger-ui feature flags here.

How to implement ToSchema for external type?

There are few ways around this that are elaborated here in detail.

How to use Rust's type aliases?

At the moment that is not possible due to there is no way to evaluate the actual type behind the type token that is visible to the proc macro code generation. This might be possible in future if a global alias registry can be implemented. Here is an issue related to the topic #766.

License

Licensed under either of Apache 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, shall be dual licensed, without any additional terms or conditions.

utoipa's People

Contributors

1lutz avatar aliscode avatar bksalman avatar jacob-pro avatar jayvdb avatar jgramoll avatar ju6ge avatar juhaku avatar kellpossible avatar kw7oe avatar kyle-mccarthy avatar lithiumflower avatar lucasgranberg avatar michael-mark avatar narayanbhat166 avatar naymoll avatar nkovacs avatar oscar6echo avatar patrick-fitzgerald avatar pbzweihander avatar rbuch703 avatar rossywhite avatar sanchithhegde avatar siketyan avatar simongoricar avatar swandog avatar sytten avatar traxys avatar trinity-1686a avatar zajko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

utoipa's Issues

Namespaced body in responses

Same issue as #152 but with body in responses

#[utoipa::path(
    responses(
        (status = 200, body = response::WillNotWork),
    ),
)]

Tried on master.

MSRV

First of all thanks for the library, I like its simple syntax!

On rustc 1.60 I have no issues, but any version below (I tried 1.55 -> 1.59) doesn't seem to build on my computer or in our CI. This is a bit blocking for us, because we can't change the version of rustc in our CI right now.

Nevertheless, I think the MSRV should be displayed in the doc or the README if it is indeed 1.60.

Support for generics?

I have a struct like this:

#[derive(Debug, Serialize, Component)]
pub struct Foo<D, R> {
    pub data: D,
    pub resources: R,
}

And I want to use it like this with a rocket request handler:

#[utoipa::path(
    get,
    context_path = "/api/v1",
    responses(
        (status = 200, description = "Foo found", body = Foo<Bar, BarResources>),
        (status = 404, description = "Foo was not found")
    ),
    params(
        ("id" = Uuid, description = "Foo id"),
    )
)]
#[get("/foo/<id>")]

I'm getting multiple errors:

  • Uuid is not a Component. I'm forced to write String instead. I wish I could write Uuid because it makes the doc clearer.
  • rustc complains about the <, it expects a comma. So I guess the proc-macro can't handle generics yet, is this planned? :)

I tried this instead:

pub type GetFooBody = Foo<Bar, BarResources>;

and then

(status = 200, description = "Foo found", body = GetFooBody),

then it compiles but I get an error in the browser when browsing the swagger doc:

Resolver error at paths./api/v1/foo/{id}.get.responses.200.content.application/json.schema.properties.data.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/D does not exist in document
Resolver error at paths./api/v1/foo/{id}.get.responses.200.content.application/json.schema.properties.resources.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/R does not exist in document

This refers to the type params D and R. Even though both Bar and BarResources are Component, the proc-macro doesn't treat them as expected. And in the swagger UI, it shows both fields' type as string :(

{
  "data": "string",
  "resources": "string"
}

Is it possible / planned to add support for generics? :)
So that I don't have to write a second set of types just for the doc, but can use the actual types that I'm using in the request handlers also for the doc.

Missing actix::web::scope support

There is many way to declare route with actix::web but for one of them is using scope (which is a must have imho, even better with macro route usage)
https://docs.rs/actix-web/3.3.3/actix_web/struct.Scope.html

/// main.rs
// app definition
  ...
  .service(web::scope("/health").service(ping_pong))

/// health.rs
#[utoipa::path(
    tag = "Health",
    responses(
        (status = 200
         , description = "It should respond with \"pong\""
         , body = String),
    ),
)]
#[get("/ping")]
pub async fn ping_pong() -> impl Responder {
  HttpResponse::Ok().body("pong")
}

What will happen here is that if we do not explicitly declared the path (with full value) it will just be generate on openapi with /ping instead of /health/ping.

Example here is simple but in an application with a lot of subscope: version, resources, action, sub resources it becomes more and more tricky to be sure to keep everything aligned (people will obviously drop declaring partial scope in favor or routes hidden in some other files),

Would it be possible to support scope?

`chrono::Duration` causes error "Expected doc attribute with one path segment"

Hi, the following code causes a build error:

#[derive(Serialize, Component)]
struct Stats {
    #[serde(serialize_with = "serialize_duration")]
    pub uptime: Duration,
}

#[utoipa::path(
    responses(
        (status = 200, description = "Uptime", body = [Stats]),
    ),
)]
#[get("/stats")]
#[tracing::instrument(skip(state))]
async fn get_stats(state: web::Data<AppState>) -> web::Json<Stats> {
    web::Json(Stats {
        uptime: chrono::Local::now() - state.started_at,
    })
}

This causes the error Expected doc attribute with one path segment for the utoipa instrumentation. This is with actix-web:

actix-web = "4.0"

utoipa = { version = "0.1.2", features = [
  "actix_extras",
  "debug",
  "chrono_types_with_format",
] }

The same error also happens with chrono_types feature, or with the chrono feature disabled.

AnyValue {} for third party types

Hello again,

I need to use some types that comes from a third party library but obviously they don't implement #[derive(Component)]. I looked at the doc and tried to do something like this

#[derive(Debug, Deserialize, Component)]
pub struct SampleStruct {
    ...
    #[component(value_type = Object)]
    pub rtp_capabilities: RtpCapabilities,
    ...
}

but sadly it stills tries to create a ref to something that doesn't exist

"rtp_capabilities": {
  "$ref": "#/components/schemas/RtpCapabilities"
},

what I would like instead is a way to specify that I want any kind of Object in #[component]. Obviously it would be better to actually have the correct type, but this would at least create a correct OpenAPI file that I can use.
Or maybe I'm missing something?

Generated OpenAPI doc uses snake_case: support for subset of serde attributes (rename_all, skip)

I've noticed that the generated open API docs component fields don't seem to respect set serde rules -- namely rust conventions use snake_case while JSON uses camelCase.
Normally I've had to add a serde attribute (#[serde(rename_all = "camelCase")]) to rename these when converting between the two. I have these added to my components currently but the generated code is still snake case.
Is there some option I can set so that the generated code will be in camel case format? Thanks!

Refactor Swagger UI configuration options

Refactor Swagger UI configuration options to support variety of other options Swagger UI configuraiton has to offer.

Tasks to do:

  • Refactor Swagger IU configuration options
  • Write the docs
  • Write tests

External not primitive types like chrono::DateTime and rust_decimal::Decimal are treated as components causing: Could not resolve pointer in Swagger doc.

I have some structs that contain types from chrono and rust_decimal. Those types give errors in the Swagger UI.

For example:

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

What is the proper way to handle these types?

Rust-Analyzer Error

Updating crates.io index
error: failed to select a version for the requirement utoipa = "^1.0.2"
candidate versions found which didn't match: 0.2.0, 0.1.2, 0.1.1, ...
location searched: crates.io index

Cargo.toml

[dependencies]
axum = "0.5.5"
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1.17", features = ["full"] }
tower = "0.4"
utoipa = "1.0.2"
utoipa-gen = "1.0.2"
utoipa-swagger-ui = "1.0.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
env_logger = "0.9.0"
log = "0.4"

utoipa::path proc_macro with additional actix route proc_macro attibutes causes compilation error

It seems the the utoipa::path does not take into account the fact actix route proc_macros can have additional attributes, I discovered it when attempting to use the wrap attribute, but it seems to cause issue with any attribute or more specifically the , used to add additional attributes

Take the route:

/// Get Todo by given todo id.
///
/// Return found `Todo` with status 200 or 404 not found if `Todo` is not found from shared in-memory storage.
#[utoipa::path(
    responses(
        (status = 200, description = "Todo found from storage", body = Todo),
        (status = 404, description = "Todo not found by id", body = TodoError)
    ),
    params(
        ("id", description = "Unique storage id of Todo")
    )
)]
#[get("/todo/{id}")]
pub(super) async fn get_todo_by_id(id: Path<i32>, todo_data: Data<TodoData>) -> impl Responder {
    let todos = todo_data.todos.lock().unwrap();
    let id = id.into_inner();

    match todos.get_one(id) {
        Ok(todo) => todo.response(),
        Err(error) => error.response(),
    }
}

And lets say we wanted to add a middleware with the wrap attribute:

/// Get Todo by given todo id.
///
/// Return found `Todo` with status 200 or 404 not found if `Todo` is not found from shared in-memory storage.
#[utoipa::path(
    responses(
        (status = 200, description = "Todo found from storage", body = Todo),
        (status = 404, description = "Todo not found by id", body = TodoError)
    ),
    params(
        ("id", description = "Unique storage id of Todo")
    )
)]
#[get("/todo/{id}", wrap = "ApiKeyAuthenticator")]
pub(super) async fn get_todo_by_id(id: Path<i32>, todo_data: Data<TodoData>) -> impl Responder {
    let todos = todo_data.todos.lock().unwrap();
    let id = id.into_inner();

    match todos.get_one(id) {
        Ok(todo) => todo.response(),
        Err(error) => error.response(),
    }
}

It will cause a compiler error:

error: unexpected token
   --> src\controllers\todo.rs:189:19
    |
189 | #[get("/todo/{id}", wrap = "ApiKeyAuthenticator")]
    |                   ^

vs without the utoipa proc_macro

#[get("/todo/{id}", wrap = "ApiKeyAuthenticator")]
pub(super) async fn get_todo_by_id(id: Path<i32>, todo_data: Data<TodoData>) -> impl Responder {
    let todos = todo_data.todos.lock().unwrap();
    let id = id.into_inner();

    match todos.get_one(id) {
        Ok(todo) => todo.response(),
        Err(error) => error.response(),
    }
}
Compiling playground-api v0.1.0 (C:\Users\bfall\Development\Rust\playground-api)
Finished dev [unoptimized + debuginfo] target(s) in 3.12s

Features are not additive

Running cargo check --all-features on master currently produces the following errors:

error[E0119]: conflicting implementations of trait `ext::ArgumentResolver` for type `ext::PathOperations`
  --> utoipa-gen/src/ext/rocket.rs:23:1
   |
23 | impl ArgumentResolver for PathOperations {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `ext::PathOperations`
   |
  ::: utoipa-gen/src/ext/actix.rs:31:1
   |
31 | impl ArgumentResolver for PathOperations {
   | ---------------------------------------- first implementation here

error[E0119]: conflicting implementations of trait `ext::PathOperationResolver` for type `ext::PathOperations`
   --> utoipa-gen/src/ext/rocket.rs:186:1
    |
186 | impl PathOperationResolver for PathOperations {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `ext::PathOperations`
    |
   ::: utoipa-gen/src/ext/actix.rs:174:1
    |
174 | impl PathOperationResolver for PathOperations {
    | --------------------------------------------- first implementation here

error[E0119]: conflicting implementations of trait `ext::PathResolver` for type `ext::PathOperations`
   --> utoipa-gen/src/ext/rocket.rs:259:1
    |
259 | impl PathResolver for PathOperations {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `ext::PathOperations`
    |
   ::: utoipa-gen/src/ext/actix.rs:217:1
    |
217 | impl PathResolver for PathOperations {
    | ------------------------------------ first implementation here

error[E0592]: duplicate definitions with name `get_type_path`
   --> utoipa-gen/src/ext/actix.rs:108:5
    |
108 |     fn get_type_path(ty: &Type) -> &TypePath {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `get_type_path`
    |
   ::: utoipa-gen/src/ext/rocket.rs:159:5
    |
159 |     fn get_type_path(ty: &Type) -> &TypePath {
    |     ---------------------------------------- other definition for `get_type_path`

error[E0592]: duplicate definitions with name `get_fn_args`
   --> utoipa-gen/src/ext/actix.rs:141:5
    |
141 |     fn get_fn_args(fn_args: &Punctuated<FnArg, Comma>) -> impl Iterator<Item = Arg> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `get_fn_args`
    |
   ::: utoipa-gen/src/ext/rocket.rs:89:5
    |
89  |     fn get_fn_args(fn_args: &Punctuated<FnArg, Comma>) -> impl Iterator<Item = Arg> + '_ {
    |     ------------------------------------------------------------------------------------ other definition for `get_fn_args`

OpenApi derive `components` section does not support namespacing

For the OpenApi derive to work all the components must be in scope because you cannot use paths.

Taking from the Axum todo example, this is possible:

use crate::todo::{Store, Todo, TodoError};

#[derive(OpenApi)]
#[openapi(
    handlers(
        todo::list_todos,
        todo::create_todo,
        todo::mark_done,
        todo::delete_todo,
    ),
    components(Todo, TodoError),
    modifiers(&SecurityAddon),
    tags(
        (name = "todo", description = "Todo items management API")
    )
)]
struct ApiDoc;

this is not:

#[derive(OpenApi)]
#[openapi(
    handlers(
        todo::list_todos,
        todo::create_todo,
        todo::mark_done,
        todo::delete_todo,
    ),
    components(crate::todo::Todo, crate::todo::TodoError),
    modifiers(&SecurityAddon),
    tags(
        (name = "todo", description = "Todo items management API")
    )
)]
struct ApiDoc;

and if you have lots of components this gets painful quickly. The handlers section already supports this.

How to reference a response body containing a dictionary?

I have a function which returns keys/values as Strings:

{
  "key": "value",
  "key2": "value2",
  "key3": null
}

How can I represent this as a response body? I want to do something like

/// Get all application options
///
/// Get a list of all application options from the database
#[utoipa::path(
    get,
    path = "/options",
    responses(
        (status = 200, description = "Sucessfully returned all application options", body = [Map<String, Option<String>>])
    )
)]

Generic types and alias

For some reason it seems that if I use some generic in struct, at some point it does not infer the type and instead expect the generic letter as type.

NB: One thing so, it seems that I cannot declared a type using genercs over some utopia attribues like MyStruct<String> (request_body, value_type etc), seems a parsing error - so I used alias, not a big deal as I already use a lot of them.

// This is dummy code, far form as complicated as in our code base but should provide enough insight I hope
type O1 = String;
type O2 = u64;

pub struct MyStruct<T> {
   pub id: String,
   pub content: OtherStruct<T>, // Please not that we could here use also another struct instead of simple alias over primitive types
}

type TopType1 = MyStruct<O1>;

#[openapi(
    handlers(...),
    components(TopType1, ...)

It returns something like this

"TopType1": {
  "type": "object",
  "required": [
    "id", "content"
  ],
  "properties": {
    "id": {
      "type": "string"
    },
    "content": {
      "$ref": "#/components/schemas/T"
    }
  },
  "description": "First round trip from requesting party to ask to build a receipt transaction"
}

I could obviously switch to use impl utoipa::Component for TopType1 { and all other sub combination (on sub structs using aliases) but as it feels to me a bit overwhelming for an already big app I am wondering if I am missing something on configuration or any property or just not supported yet.

Any insight is welcome, thanks.

How to configure API introduction?

  1. How can I specify what it shows for "License" / how can I remove it?

image

  1. How can I add a custom API introduction text here (using markdown)? I tried with a doc comment (using #[doc = include_str!("API-Intro.md")]) for my main ApiDoc struct that has #[derive(OpenApi)], but the doc comment seems to be ignored by the generator, any idea why?

  2. How can I specify the html <title> for the generated doc? :)

Inline schemas

I'm dealing with an API that has a unique type for each response body, having them all as components is a bit of a headache, and they won't be re-used in other endpoints. It would be nice to have a way to optionally inline schema definitions. Perhaps with a special wrapper like inline(), the path macro could insert the schema for a derived Component inline rather than attempting to reference it?

oas3-valid-oas-content-example 'example' property should match exactly one schema in oneOf

I'm not very fluent in the OpenAPI specifications, so I wanted to see if I was doing something wrong.

Using the example code, but saving the json to a file instead of using the swagger ui

When adding the OpenAPI json into insomnia it reports the error for each of the ErrorResponse examples:
oas3-valid-oas-content-example 'example' property should match exactly one schema in oneOf

image

IntoParams doesn't work if not in the same file as handler

I have the following handler:

#[utoipa::path(
    context_path = "/api/v1/private/workers/{worker_id}/pipe_transports",
    responses(
        (status = 200, description = "Pipe Transport created"),
    ),
    tag = "pipe_transports"
  )]
#[post("")]
#[tracing::instrument(skip(state))]
pub async fn create(
    req_path: Path<request::CreatePipeTransportRequestPath>,
    state: Data<crate::state::AppStateHandle>,
) -> Result<Json<response::CreatePipeTransportResponse>, AppError> {
    .......
}

with the following struct for the Path in a different file (request::CreatePipeTransportRequestPath)

#[derive(Debug, Deserialize, Serialize, IntoParams)]
pub struct CreatePipeTransportRequestPath {
    pub worker_id: WorkerId,
}

this result in the following error:

the trait bound handlers::workers::pipe_transports::request::CreatePipeTransportRequestPath: utoipa::ParameterIn is not satisfied
the trait utoipa::ParameterIn is not implemented for handlers::workers::pipe_transports::request::CreatePipeTransportRequestPath

but, if I put CreatePipeTransportRequestPath in the same file as my create handler, I don't have any error.

Allow `serde_json::Value` as `any` or `object`

In my API I would like a way to send any data. Something like this

    // Optional data, can be any JSON value
    #[serde(skip_serializing_if = "Option::is_none")]
    #[component(value_type = Any)]
    data: Option<serde_json::Value>,

Unfortunately swagger-ui throws an error and it does not display it properly

Support for form params and fields that use generics

I have this type that occurs in a handler param:

#[derive(Debug, FromForm, Component)]
pub struct FilterParams {
    pub ids: Option<VecParam<Uuid>>,
    pub archived: Option<bool>,
    #[form(field = "dueDateStart")]
    pub due_date_start: Option<i64>,
    #[form(field = "dueDateEnd")]
    pub due_date_end: Option<i64>,
    #[form(field = "periodStart")]
    pub period_start: Option<String>,
    #[form(field = "periodEnd")]
    pub period_end: Option<String>,
}
    params(
        ("filter_params" = FilterParams, description = "Filter parameters"),
    )
)]
#[get("/results?<filter_params..>")]

I see that the field renaming attributes are ignored by the Component derive macro. Could you modify it to take them into account? :)

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.

Implement general serve swagger-ui capability & warp-example

Tracking issue for implementing general serve Swagger UI capability. Currently there is no easy way to use the utoipa-swagger-ui crate with other than actix-web framework. PR's added here will change that and plan is to provide easy to use interface for serving Swagger UI with other frameworks.

Also to demonstrate that there will be new example written with warp framework.

struct is not supported in `trait`s or `impl`s` or: where can `utoipa::path()` be used?

I have an existing warp project that I want to add an OpenAPI spec to. But I had a problem: the handler functions for filters are in traits, so when I tried to use the utoipa::path macro in front of the handler function, I got this compilation error:

error: struct is not supported in `trait`s or `impl`s
   --> cloud_backend/src/crud.rs:465:5
    |
465 | /     #[utoipa::path(get,
466 | |         path = "dataset/",
467 | |         responses(
468 | |             (status = 200, body = String)
469 | |         )
470 | |     )]
    | |______^
    |
    = help: consider moving the struct out to a nearby module scope
    = note: this error originates in the attribute macro `utoipa::path` (in Nightly builds, run with -Z macro-backtrace for more info)

error: implementation is not supported in `trait`s or `impl`s
   --> cloud_backend/src/crud.rs:465:5
    |
465 | /     #[utoipa::path(get,
466 | |         path = "dataset/",
467 | |         responses(
468 | |             (status = 200, body = String)
469 | |         )
470 | |     )]
    | |______^
    |
    = help: consider moving the implementation out to a nearby module scope
    = note: this error originates in the attribute macro `utoipa::path` (in Nightly builds, run with -Z macro-backtrace for more info)

My interpretation of this is that the utoipa::path() macro writes a struct definition, which is then illegal because Rust does not allow those to be defined inside a trait.

Do I have any alternatives here? It would be difficult for me to rewrite the project to extract the handlers out of the trait. Would I have to write stub handlers that call the trait methods, solely for the purpose of being annotated by utoipa?

My ideal option would be if I could annotate Filter objects, because that's the most basic part of my code.

Alternatively--can I write these structs myself? That may be less exciting but would make this work.

I apologize if I misunderstood something; I'm somewhat new to Rust and warp, and I just found out about utoipa today.

Using utoipa-swagger-ui with rocket 0.4.10

Hi,
when using utoipa-swagger-ui with rocket 0.4.10, I'm getting an error:

error[E0277]: the trait bound `Vec<Route>: std::convert::From<SwaggerUi>` is not satisfied
   --> web/src/cli/web.rs:486:13
    |
484 |         rocket.mount(
    |                ----- required by a bound introduced by this call
485 |             "/",
486 |             SwaggerUi::new("/swagger-ui/<_..>").url("/api-doc/openapi.json", ApiDocV1::openapi()),
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<SwaggerUi>` is not implemented for `Vec<Route>`
    |
    = help: the following implementations were found:
              <Vec<T, A> as std::convert::From<VecDeque<T, A>>>
              <Vec<T, A> as std::convert::From<std::boxed::Box<[T], A>>>
              <Vec<T> as std::convert::From<&[T]>>
              <Vec<T> as std::convert::From<&mut [T]>>
            and 9 others
    = note: required because of the requirements on the impl of `Into<Vec<Route>>` for `SwaggerUi`
note: required by a bound in `rocket::Rocket::mount`
   --> /usr/src/.cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.4.10/src/rocket.rs:515:21
    |
515 |     pub fn mount<R: Into<Vec<Route>>>(mut self, base: &str, routes: R) -> Self {
    |                     ^^^^^^^^^^^^^^^^ required by this bound in `rocket::Rocket::mount`

I see that utoipa-swagger-ui depends on rocket 0.5.0-rc.1:

rocket = { version = "0.5.0-rc.1", features = ["json"], optional = true }

Is it still possible to use it with rocket 0.4.10 somehow?

I tried

[patch.crates-io]
rocket = "0.4.10"

to replace the dependency, but I'm still getting the same error.

How can I declare File data?

My utoipa doc tag is like

#[utoipa::path(
	context_path = "/posts",
    tag="Post",
    request_body(content = CreatePostRequest, description = "New post data", content_type = "multipart/form-data"),
    responses(
        (status = 200, description = "create new post", body = String)
    ),
	security(
        ("authorization"=[])
    )
)]

and the content structure is

#[derive(Debug, Component)]
pub struct CreatePostRequest {
    pub body: Option<String>,
    pub tags: Option<Vec<u32>>,
    #[component(format = ComponentFormat::Binary)]
    pub audio_file: Option<File>,
}

How can I set audio_file field as a file upload?

Integrate with Rocket framework

Tracking issue for enhanced integration for rocket framework similar to actix_extras. Currently rocket framework is supported same way as warp or tide where all documentation must be provided with utoipa::path path attribute macro, which means that rockets own macros cannot be "reused" in utoipa to generate the documentation.

Possibly changing list of actions:

  1. Implement rocket_extras feature flag. This should provide support for resolving path parameters and query parameters from function arguments as well as resolving path.
  2. Implement rocket example.
  3. Add rocket utoipa-swagger-ui integration.
  4. Update docs and explain what is supported.

Improve actix-web integration with resolving path and query parameters

Tracking issue for improving actix-web integration. Goal for improvement is to enable path and query parameters to be resolved from structs.

Task to do:

  • Implement IntoParams trait with logic which is able to resolve the correct parameters from IntoParams trait.
  • Implement IntoParams trait derive logic.
  • Write tests
  • Update docs
  • Update examples

Component properties are not deserialized

Using serde_json::from_str, the properties of components are not deserialized. See the example below, where an openapi spec is serialized and then deserialized, loosing component properties in the process.

use utoipa::openapi::{
    schema::{ComponentType, ComponentsBuilder, ObjectBuilder, PropertyBuilder},
    security::SecurityScheme,
    OpenApi, {Info, OpenApiBuilder, Paths},
};

fn main() {
    let openapi = OpenApiBuilder::new()
        .info(Info::new("", "1.0.0"))
        .paths(Paths::new())
        .components(Some(
            ComponentsBuilder::new()
                .components_from_iter(vec![(
                    "Comp",
                    ObjectBuilder::new()
                        .property(
                            "name",
                            PropertyBuilder::new().component_type(ComponentType::String),
                        )
                        .required("name"),
                )])
                .security_scheme("TLS", SecurityScheme::MutualTls { description: None })
                .build(),
        ))
        .build();

    let serialized_openapi = serde_json::to_string(&openapi).unwrap();
    println!("{}", serialized_openapi);

    let deserialized_openapi: OpenApi = serde_json::from_str(serialized_openapi.as_str()).unwrap();
    println!("{}", serde_json::to_string(&deserialized_openapi).unwrap());
}
{"openapi":"3.0.3","info":{"title":"","version":"1.0.0"},"paths":{},"components":{"schemas":{"Comp":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}},"securitySchemes":{"TLS":{"type":"mutualTLS"}}}}
{"openapi":"3.0.3","info":{"title":"","version":"1.0.0"},"paths":{},"components":{"schemas":{"Comp":{"type":"object"}},"securitySchemes":{"TLS":{"type":"mutualTLS"}}}}

Derive Component doesn't support tuples?

#[derive(Component)]
struct Bar {
    foo: (String, String)
}

or

#[derive(Component)]
struct Bar {
    foo: (String)
}

I get the following error message:

unexpected type in component part get type path, expected one of: Path, Reference, Group

Axum extras integration

Tracking issue for deeper integration between axum and utoipa. Integration is similar to one existing for actix-web and rocket frameworks.

Tasks:

  • Add axum integration for Swagger in utoipa-swagger-ui
  • Add axum path and query parameter integration to utoipa (utoipa-gen) and write tests.
  • Write docs

#[serde(flatten)] may not be supported (yet)

If we design a struct with the special instruction flatten of serde it is not flatten during schema creation.

#[derive(Serialize, Deserialize, Debug, Clone, Component)]
pub struct SomePayload {
  some_id: String,
  
  #[serde(flatten)]
  extras: ExtraContent,
}

So would it be possible to tackle that point, maybe like the example, format, write_only?

#[derive(Serialize, Deserialize, Debug, Clone, Component)]
pub struct SomePayload {
  some_id: String,

  #[serde(flatten)]
  #[component(nested = true)]
  extras: ExtraContent,
}

Conflicting implementations of `utoipa::ParameterIn`

This code used to compile in version "0.1.2":

#[derive(Serialize, Deserialize, Component)]
struct WorkflowId(u64);

#[utoipa::path(get, path = "/workflow/{id}")]
async fn load_workflow(id: Path<WorkflowId>) {}

#[utoipa::path(delete, path = "/workflow/{id}")]
async fn delete_workflow(id: Path<WorkflowId>) {}

In the current version "1.0.2" the compilers yields

error[E0119]: conflicting implementations of trait `utoipa::ParameterIn` for type `WorkflowId`
  --> src/main.rs:12:35
   |
9  | async fn load_workflow(id: Path<WorkflowId>) {}
   |                                 ---------- first implementation here
...
12 | async fn delete_workflow(id: Path<WorkflowId>) {}
   |                                   ^^^^^^^^^^ conflicting implementation for `WorkflowId`

Swagger-UI and default petstore example

This lib is awesome but I'm having a hard time getting the swagger-ui part to do what I need. Is there any configuration I can set so that the swagger-ui endpoint displays my generated swagger doc vs displaying the petstore example? I've been able to see my generated swagger-ui by using the explore button and navigating to the path but I'd really like to serve my doc up by default and I'm not sure if I can do that with this lib. Thanks!

ETA: I have tried swagger-ui/api-doc/openapi.json but that just gets me a 404

Custom tag names for routes.

Route tags/namespaces at the moment are generated by what is provided in handlers. Might there be a way to specify renames for these tags so that they can be more clear? For example

	tags(
		(paths = [route::admin::user], name = "Admin", description = "Admin endpoints."),
	),

on openapi for example?

[Unexpected] Examples are rerendered

I added some data examples on my manual schema (Uuid, type Alias etc) through the impl Modify behavior, one of this example could be something like this

/// random values, could even be generated from proptest/quickcheck for struct
uuid_p.example = Some(serde_json::Value::String(Uuid::new_v4().to_string()));

Surprisingly I found that each time I requested the openapi.json file, my examples are differents (this could come from the use of Modify I did not dig that deeper).

IMHO this is not a big issue but still it should not be the case.

#[serde(rename_all = "lowercase")] fails to compile

#[derive(Deserialize, Serialize, utoipa::Component)]
#[serde(rename_all = "lowercase")]
pub enum MyType {
    CaseOne,
    CaseTwo,
}

yields

unexpected rename rule, expected one of: \"lowercase\", \"UPPERCASE\", \"Pascal\", \"camelCase\", \"snake_case\", \"SCREAMING_SNAKE_CASE\", \"kebab-case\", \"SCREAMING-KEBAB-CASE\"

IntoParams style container attribute

It would be nice to have a single attribute for all fields of a struct deriving IntoParams for style.

Perhaps something like:

#[derive(Deserialize, IntoParams)]
#[param(style = Form)]
struct Filter {
    /// Foo database id.
    id: u64,
    /// Datetime since foo is updated.
    since: Option<String>,
}

Also what happens when different parameters in the same path have different parameter styles? Perhaps the style attribute only makes sense on the container level?

Wishlist: derive response body

I've noticed it's fairly easy to forget to add things to the macros -- for example I generated an incorrect swagger doc (my fault, not the tools) by leaving out a body = SomeBodyType.
In some future version of this tool it would be nice to have this be a bit more foolproof and if body isn't specified it could assume the body is the return type of the annotated function. This leaves one less place where the actual source code and generated swagger docs could diverge. If someone did want to (or needed to) override that behavior they could provide the body in the macro as usual.
I know it might get tricky with the different return codes -- maybe only for 200. IME our return types from the handlers have always corresponded directly to the response we've specified in the swagger doc.
Thoughts?

IntoParams is not satisfied

I have some actix web query structs that I cannot derive IntoParams because only primitive and String types are supported. Is there a way of skipping that and just providing the query params in the params(...) section in the #[path...] derive?

Improve OpenAPI parameter support by implementing feasible configuration options

Tracking issue for improving OpenAPI parameter configuration options. Currently configuration options are limited to pretty much defaults. Actions taken within this issue will address this fact and provide feasible configuration options for the parameters.

Action points

  • Improve IntoParams derive to accept param arguments to configure parameter
  • Improve #[utoipa::path(...)] params() section to allow more configuration options for parameters
  • Write tests
  • Update docs

Derive Component for HashMap field

#[derive(Component)]
struct Foo {
    field: HashMap<String, u64>,
}

results in:

{
  "properties": {
    "field": {
      "type": "object"
    }
  },
  "required": [
    "field"
  ],
  "type": "object"
}

It looks like the spec https://swagger.io/specification/#schema-object should support the additionalProperties type that can be used to provide a proper schema for the HashMap?

{
  "properties": {
    "field": {
      "type": "object",
      "additionalProperties": {
        "type": "integer",
        "format": "uint64",
        "minimum": 0
      }
    }
  }
  "required": [
    "field"
  ],
  "type": "object",
}

Add support for API method description

In the sample petstore swagger1, there is a description for each supported API method. For example, Add a new pet to the store 2.

So far I found no way to add such a method description in utoipa. I tried different ways to add a description or summary to the handler definition, or to the #[utoipa::path(...)] annotations, but couldn't get it to work. Looks like its currently not supported.

Can you please add support for that? Would be very useful.

Thanks.

Footnotes

  1. https://petstore.swagger.io

  2. https://petstore.swagger.io/#/pet/addPet

todo-warp example broken

The todo-warp example is currently broken, it shows an empty page on both the http://localhost:8080/swagger-ui/ and http://localhost:8080/swagger-ui paths. Perhaps this is related to #171 ? Checking out the previous commit 13c9149, the example runs successfully.

Override value_type for IntoParams

I need to override some types in my Path struct but it's not currently possible to do that*

#[derive(Debug, Deserialize, IntoParams)]
pub struct SomeStruct {
    #[param(value_type = Uuid)]
    pub some_id: SomeUUIDWrapper,
}

It would be great to be able to do the same thing as with Component

#[derive(Debug, Deserialize, Component)]
pub struct SomeStruct {
    #[component(value_type = Uuid)]
    pub some_id: SomeUUIDWrapper,
}

*for now I'll just remove the IntoParams and define params in path and stay on 1.1.0 as it's not possible on master to have a Path<SomeStruct> that doesn't implement IntoParams

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.