swagger-ui icon indicating copy to clipboard operation
swagger-ui copied to clipboard

OpenAPI 3.1.0 support: OpenAPI 3.1 type: […, "null"] incomplete support in parameters

Open commonism opened this issue 2 years ago • 8 comments

Q&A (please complete the following information)

  • OS: Ubuntu
  • Browser: Firefox
  • Version: 114.0.2
  • Method of installation: apt
  • Swagger-UI version: https://editor-next.swagger.io/
  • Swagger/OpenAPI version: 3.1

Content & configuration

Example Swagger/OpenAPI definition: https://gist.github.com/commonism/7adcb666278f4070b8f7aabd456faa43

openapi: 3.1.0
info:
  title: ''
  version: 0.0.0
servers:
  - url: http://127.0.0.1/api

security:
  - {}

paths:
  /{path}:
    parameters:
      - $ref: "#/components/parameters/path"
      - $ref: '#/components/parameters/cookie'

    post:
      operationId: post
      requestBody:
        description: "!"
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Any'

      responses:
        '200':
          description: "!"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Any"

      callbacks:
        onData:
          '{$request.query.callbackUrl}/data':
            post:
              requestBody:
                description: "!"
                content:
                  application/json:
                    schema:
                      $ref: "#/components/schemas/Any"
              responses:
                '200':
                  description: "!"
                  content:
                    application/json:
                      schema:
                        $ref: "#/components/schemas/Any"



    get:
      operationId: get
      parameters:
      - $ref: "#/components/parameters/query"
      - $ref: "#/components/parameters/header"

      responses: &resp
        '200':
          description: "!"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Any'
          headers:
            head:
              $ref: "#/components/headers/header"



components:
  schemas:
    Any:
      type: [integer, string, object, array, boolean, "null"]
      items:
        $ref: "#/components/schemas/Any"
      properties:
        next:
          $ref: "#/components/schemas/Any"

  headers:
    header:
      schema:
          $ref: "#/components/schemas/Any"
      style: simple
      explode: false

  parameters:

    query:
      in: query
      name: query
      style: deepObject
      explode: true
      required: true
      schema:
        $ref: "#/components/schemas/Any"

    path:
      in: path
      name: path
      required: true
      schema:
        type: [string, "null"]

    header:
      in: header
      name: header
      required: true
      schema:
        type: [string, "null"]

    cookie:
      in: cookie
      name: cookie
      required: true
      schema:
        type: [string, "null"]

Describe the bug you're encountering

The rendering of the parameters does not reflect the use of OpenAPI 3.1 type as a list. The use of "null" as list element to indicate "nullable" is not reflected as well.

To reproduce...

Steps to reproduce the behavior: Paste the yaml to swagger editor.

Expected behavior

Screenshots

Bad types for Response & header image

all Parameters image

Callback Response image

but works for a RequestBody image

Additional context or thoughts

type: […, "null"] is the preferable way to define nullable in v3.1, using the alternative anyOf: […,{type: "null"}] instead results in a additional/unnecessary level of indirection in models generated from description documents.

commonism avatar Jul 24 '23 19:07 commonism

I'm confused:

  1. Here https://github.com/OAI/OpenAPI-Specification/issues/3148 @handrews states that both ways (type: […, "null"] and anyOf: […,{type: "null"}]) are are supported in OAS 3.1
  2. But here https://github.com/smallrye/smallrye-open-api/issues/2094#issuecomment-2539374215 @MikeEdgar describes convincingly that combining $ref with type: […, "null"] is invalid
  3. @commonism says above "...type: […, "null"] is the preferable way to define nullable in v3.1, using the alternative anyOf: […,{type: "null"}] ..."

So openApi generated by smallrye with Quarkus3.17 is valid according to JSON-Schema (using the anyOf-way) but it's rendered badly in swagger-ui (real type is shown deeply nested, see https://github.com/smallrye/smallrye-open-api/issues/2094#issue-2722703834).

The question is: Is it possible to get a nicely rendered swagger-ui (with type-info not deeply nested but at the top like in the screenshot) with valid openApi.yaml according to [2] grafik (screenshot from https://github.com/smallrye/smallrye-open-api/issues/2094)

ChMThiel avatar Dec 13 '24 07:12 ChMThiel

@ChMThiel there isn't any conflict here. What @MikeEdgar seems to be pointing out is that if you $ref a schema with type: "string", you can't just stick a type: "null" next to the $ref have it be the same as an anyOf. This is true: adjacent keyword results are ANDed (like allOf) and not ORed (like anyOf).

So you have to use anyOf to "add" type: "null" to the set of available types. This works because no other keywords apply to type: "null", so it doesn't really matter what else is in the schema containing type: "string" on the other end of the $ref.

What's different about the example that I answered is that the two options were 1.) have both types in the same type keyword (type: ["string", "null"]) or 2.) put each type in a separate anyOf branch. At no point in that issue I answered does it say that you can just put type: "null" next to a $ref and have that work.

handrews avatar Dec 13 '24 16:12 handrews

Ok, than anyOfis the way to describe nullability. But if so, the null/type-info is rendered in swagger-ui in a much less comprehensive way, as it was using OpenApi 3.0.3 (that's why i got a ticket in my company, see https://github.com/smallrye/smallrye-open-api/issues/2094):

  • 3.0.3 grafik
  • 3.1.0 grafik

Afaik the anyOf was orginaly intend to be used for multiple types (like inheritance: e.g. anyOf plant: tree, bush, flower, etc.) In such a case the 3.1.0-way of rendereing is just fine.

The special case that a value has just one type (in my example a LocalTime) but can be null is a very common thing (at least in my APIs). Rendering that special case in swagger-ui as 'ordinary' anyOf's is imho not optimal (compared with the 'old' 3.0.3 way: in 3.0.3 you see the 'real' local-time type on first level; in 3.1.0 you have expand/klick all the way down, at first glance on first level it looks like a string-value).

Isn't there a posibility that swagger-ui renders the type/null-information at least for one type/null anyOf's in a better way and not so deeply nested?

ChMThiel avatar Dec 16 '24 06:12 ChMThiel

When this was written - swagger-ui does not support type-lists as type: […, null]. `anyOf: [{type: null}] was a workaround to have swagger-ui render nullable in 3.1.

This has changed over time:

Some improved: image

Others … not: image

commonism avatar Dec 16 '24 08:12 commonism

Afaik the anyOf was orginaly intend to be used for multiple types (like inheritance: e.g. anyOf plant: tree, bush, flower, etc.) In such a case the 3.1.0-way of rendereing is just fine.

anyOf is just a non-exclusive OR. It has no inherent semantics beyond that.

When using null, as long as the other option(s) don't accept null then you can use oneOf safely as an alternative. But do be careful with this- a schema like {"minimum": 1} will accept null (but {"type": "integer", "minimum": 1} will not).

handrews avatar Dec 16 '24 21:12 handrews

Afaik the anyOf was orginaly intend to be used for multiple types (like inheritance: e.g. anyOf plant: tree, bush, flower, etc.) In such a case the 3.1.0-way of rendereing is just fine.

anyOf is just a non-exclusive OR. It has no inherent semantics beyond that.

When using null, as long as the other option(s) don't accept null then you can use oneOf safely as an alternative. But do be careful with this- a schema like {"minimum": 1} will accept null (but {"type": "integer", "minimum": 1} will not).

I'm using quarkus that generates the openApi.yml - with anyOf for the null-info. So (even if i wanted to) i have very limited options to change this. And as the discussion here shows, using anyOf seem to be the right way to do that (so i closed https://github.com/smallrye/smallrye-open-api/issues/2094).

All i`m pointing out is that the type-info in the special case that a value has just one type but can be null, is rendered not as comprehensive as it was in OpenAPi 3.0.3.

ChMThiel avatar Dec 17 '24 07:12 ChMThiel

hello, i'm looking for advice since i fail to find it in the spec ... is it that the order of {"type": "null"} in either anyOf or oneOf is relevant with regards to nullability? i have this mimal working example:

{
  "openapi": "3.1.0",
  "paths": {
    "/foo": {
      "get": {
        "operationId": "foo",
        "parameters": [
          {
            "name": "just_a_string",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "optional_from_enum",
            "in": "query",
            "description": "quux",
            "required": false,
            "schema": {
              "oneOf": [
                { "type": "null" },
                { "$ref": "#/components/schemas/Role" }
              ]
            }
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Role": {
        "type": "string",
        "enum": [ "abc", "def", "ghi" ]
      }
    }
  }
}

which renders (incorrectly) like this (swagger ui 5.22.0):

Image

however, merely swapping the order of the types in the oneOf gives me the expected UI:

...
          {
            "name": "optional_from_enum",
            "in": "query",
            "description": "quux",
            "required": false,
            "schema": {
              "oneOf": [
                { "$ref": "#/components/schemas/Role" },
                { "type": "null" }
              ]
            }
          }
...

Image

i'm surprised the order matters, but i'm not sure whether it's a "spec thingy" or just a deficiency of swagger-ui. I hope somebody can clarify.

xitep avatar Jun 01 '25 15:06 xitep

just a deficiency of swagger-ui

commonism avatar Jun 03 '25 07:06 commonism

How could we help to get the correct syntax working ?

gmli avatar Jul 07 '25 12:07 gmli