utoipa icon indicating copy to clipboard operation
utoipa copied to clipboard

IntoParams optional parameter with #[serde(default)]

Open kellpossible opened this issue 2 years ago • 3 comments

At the moment if a field is marked with #[serde(default)] it is still noted as a required field in the generated openapi schema.

kellpossible avatar Jun 28 '22 01:06 kellpossible

Yes serde(default) is not regarded in any way at the moment. Support for this could be added though. I have a plan to support flatten too so they could be implemented in tandem.

juhaku avatar Jul 01 '22 14:07 juhaku

I am interested in working on this, I will update this issue with my progress. Don't let it stop anyone else though! This is my first contribution to this project so it will be slow.

tomharmon avatar Jul 06 '22 22:07 tomharmon

Alright its good to have more contributions :) Speeds up the overall development.

juhaku avatar Jul 07 '22 15:07 juhaku

I might have missed something, but I am unable to get a field to be documented as optional when using the IntoParams derive macro. To make sure this was not a regression, I have used the 2.3.0 release where #339 was published:

# Cargo.toml
[package]
name = "optional-utoipa-param"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = "1.0.188"
utoipa = "=2.3.0"

I am currently using the code below.

// src/main.rs
use serde::Deserialize;
use utoipa::{IntoParams, OpenApi};

#[derive(Deserialize, IntoParams)]
pub struct Params {
    #[into_params(required = false)]
    pub a: String,
    pub b: Option<String>,
    #[serde(default)]
    pub c: String,
    #[serde(default)]
    pub d: Option<String>,
}

#[utoipa::path(get, path = "/test", params(Params))]
pub fn test() {
    unimplemented!();
}

#[derive(OpenApi)]
#[openapi(paths(test))]
struct ApiDoc;

fn main() {
    println!("{}", ApiDoc::openapi().to_json().unwrap());
}

However, with cargo build && ./target/debug/optional-utoipa-param | jq, we can see that all four fields are marked as required:

{
  "openapi": "3.0.3",
  "info": {
    "title": "optional-utoipa-param",
    "description": "",
    "contact": {
      "name": ""
    },
    "license": {
      "name": ""
    },
    "version": "0.1.0"
  },
  "paths": {
    "/test": {
      "get": {
        "tags": [
          "crate"
        ],
        "description": "",
        "operationId": "test",
        "parameters": [
          {
            "name": "a",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "b",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "c",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "d",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {},
        "deprecated": false
      }
    }
  }
}

Could you tell me if there is anything I missed?

qsantos avatar Sep 19 '23 10:09 qsantos

To anyone wondering, this is because the parameters_in default to path, where all fields are always required. This fixes the issue I had:

diff --git a/src/main.rs b/src/main.rs
index aa9ff69..b391236 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,6 +2,7 @@ use serde::Deserialize;
 use utoipa::{IntoParams, OpenApi, ToSchema};

 #[derive(Deserialize, IntoParams, ToSchema)]
+#[into_params(parameter_in = Query)]
 pub struct Params {
     #[into_params(required = false)]
     pub a: String,

qsantos avatar Sep 19 '23 11:09 qsantos