Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: Optional types generate incorrect OpenAPI schemas #1210

Closed
nilsso opened this issue Feb 17, 2023 · 2 comments · Fixed by #1225
Closed

Bug: Optional types generate incorrect OpenAPI schemas #1210

nilsso opened this issue Feb 17, 2023 · 2 comments · Fixed by #1225
Assignees
Labels
Bug 🐛 This is something that is not working as expected Triage Required 🏥 This requires triage

Comments

@nilsso
Copy link
Contributor

nilsso commented Feb 17, 2023

Describe the bug

  1. Optional types in query parameters don't seem to play well with OpenAPI, resulting in a bad schema, except that upon some more testing if there is more than one non-None type in the annotation it works.

A query parameter annotated int | None produces incorrectly:

{ "oneOf": [
    { "type": null" },
    { "oneOf": [] }
]}

While one annotated int | str | date | None produces correctly:

{ "oneOf": [
    { "type": "null" },
    { "type": "integer" },
    { "type": "string" },
    { "type": "string", "format": "date" }
]}
  1. Additionally, optional types are being assumed to be optional parameters, resulting in "required": false regardless of whether the parameter has a default value or not.

  2. And finally, route return types that are optional are mostly correct, but for some reason include an empty node (e.g. { "oneOf": [ { "type": "null" }, { "type": "integer" }, {} }). This results in /schema/redoc including a possible type of Any.

To Reproduce

(I ran this on 3.11, but IIRC 3.8+ for | None should work. I also tried with Optional and got the same results.)

As a minimal example:

# starlite_optionals.py
from datetime import date

from starlite import Starlite, get


@get("/a")
def a(v: int | None) -> int | None:
    return v


@get("/b")
def b(v: int | str | date | None) -> int | str | date | None:
    return v


@get("/c")
def c(v: int | None = None) -> int | None:
    return v


@get("/d")
def d(v: int | str | date | None = None) -> int | str | date | None:
    return v


app = Starlite(route_handlers=[a, b, c, d], debug=True)

Check the generated schema below, or by running uvicorn starlite_optionals:app and navigating to http://127.0.0.1:8000/schema/openapi.json and/or http://127.0.0.1:8000/schema/.

Generated OpenAPI schema.
{
  "openapi": "3.1.0",
  "info": {
    "title": "Starlite API",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "paths": {
    "/a": {
      "get": {
        "operationId": "AA",
        "parameters": [
          {
            "name": "v",
            "in": "query",
            "required": false,
            "deprecated": false,
            "allowEmptyValue": false,
            "allowReserved": false,
            "schema": {
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "oneOf": []
                }
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Request fulfilled, document follows",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "integer"
                    },
                    {}
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request syntax or unsupported method",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status_code": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    },
                    "extra": {
                      "additionalProperties": {},
                      "type": [
                        "null",
                        "object",
                        "array"
                      ]
                    }
                  },
                  "type": "object",
                  "required": [
                    "detail",
                    "status_code"
                  ],
                  "description": "Validation Exception",
                  "examples": [
                    {
                      "status_code": 400,
                      "detail": "Bad Request",
                      "extra": {}
                    }
                  ]
                }
              }
            }
          }
        },
        "deprecated": false
      }
    },
    "/b": {
      "get": {
        "operationId": "BB",
        "parameters": [
          {
            "name": "v",
            "in": "query",
            "required": false,
            "deprecated": false,
            "allowEmptyValue": false,
            "allowReserved": false,
            "schema": {
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "type": "integer"
                },
                {
                  "type": "string"
                },
                {
                  "type": "string",
                  "format": "date"
                }
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Request fulfilled, document follows",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "integer"
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "string",
                      "format": "date"
                    },
                    {}
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request syntax or unsupported method",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status_code": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    },
                    "extra": {
                      "additionalProperties": {},
                      "type": [
                        "null",
                        "object",
                        "array"
                      ]
                    }
                  },
                  "type": "object",
                  "required": [
                    "detail",
                    "status_code"
                  ],
                  "description": "Validation Exception",
                  "examples": [
                    {
                      "status_code": 400,
                      "detail": "Bad Request",
                      "extra": {}
                    }
                  ]
                }
              }
            }
          }
        },
        "deprecated": false
      }
    },
    "/c": {
      "get": {
        "operationId": "CC",
        "parameters": [
          {
            "name": "v",
            "in": "query",
            "required": false,
            "deprecated": false,
            "allowEmptyValue": false,
            "allowReserved": false,
            "schema": {
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "oneOf": []
                }
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Request fulfilled, document follows",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "integer"
                    },
                    {}
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request syntax or unsupported method",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status_code": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    },
                    "extra": {
                      "additionalProperties": {},
                      "type": [
                        "null",
                        "object",
                        "array"
                      ]
                    }
                  },
                  "type": "object",
                  "required": [
                    "detail",
                    "status_code"
                  ],
                  "description": "Validation Exception",
                  "examples": [
                    {
                      "status_code": 400,
                      "detail": "Bad Request",
                      "extra": {}
                    }
                  ]
                }
              }
            }
          }
        },
        "deprecated": false
      }
    },
    "/d": {
      "get": {
        "operationId": "DD",
        "parameters": [
          {
            "name": "v",
            "in": "query",
            "required": false,
            "deprecated": false,
            "allowEmptyValue": false,
            "allowReserved": false,
            "schema": {
              "oneOf": [
                {
                  "type": "null"
                },
                {
                  "type": "integer"
                },
                {
                  "type": "string"
                },
                {
                  "type": "string",
                  "format": "date"
                }
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Request fulfilled, document follows",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "integer"
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "string",
                      "format": "date"
                    },
                    {}
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request syntax or unsupported method",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status_code": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string"
                    },
                    "extra": {
                      "additionalProperties": {},
                      "type": [
                        "null",
                        "object",
                        "array"
                      ]
                    }
                  },
                  "type": "object",
                  "required": [
                    "detail",
                    "status_code"
                  ],
                  "description": "Validation Exception",
                  "examples": [
                    {
                      "status_code": 400,
                      "detail": "Bad Request",
                      "extra": {}
                    }
                  ]
                }
              }
            }
          }
        },
        "deprecated": false
      }
    }
  }
}
@nilsso nilsso added Bug 🐛 This is something that is not working as expected Triage Required 🏥 This requires triage labels Feb 17, 2023
@nilsso
Copy link
Contributor Author

nilsso commented Feb 21, 2023

@all-contributors please add @nilsso for code

@allcontributors
Copy link
Contributor

@nilsso

I've put up a pull request to add @nilsso! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🐛 This is something that is not working as expected Triage Required 🏥 This requires triage
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant