OpenAPITools / OpenAPITools/openapi-generator
[BUG] [PYTHON-FASTAPI] required request body generates an optional body
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
When defining a requestBody as required via the requestBody.required: true property in openapi (https://swagger.io/docs/specification/v3_0/describing-request-body/describing-request-body/#requestbody-content-and-media-types), I expect the generated server to generate a required body. However, the generated server sets the default value of that parameter to None, so FastAPI marks that body as optional.
Pydantic reference: https://fastapi.tiangolo.com/tutorial/body/#create-your-data-model
openapi-generator version
7.12.0
I don't think this is a regression, as looking through the git history, didn't seem to have ever worked
OpenAPI declaration file content or url
openapi: 3.0.1
info:
title: Sample API
version: 1.0.0
paths:
/pets:
post:
summary: Add a new pet
requestBody:
description: Optional description in *Markdown*
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
responses:
"201":
description: Created
components:
schemas:
Pet:
type: object
required:
- name
properties:
name:
type: string
tag:
type: string
Generation Details
openapi-generator generate -i openapi.yml -g python-fastapi -o server
This generates something like this
@router.post(
"/pets",
responses={
201: {"description": "Created"},
},
tags=["default"],
summary="Add a new pet",
response_model_by_alias=True,
)
async def pets_post(
pet: Annotated[Pet, Field(description="Optional description in *Markdown*")] = Body(None, description="Optional description in *Markdown*"),
) -> None:
note the pet field value: Body(None, description="Optional description in *Markdown*"), the Body has None as a default argument, so FastAPI marks that as optional.
The expected field value should be something like just Body(description="Optional description in *Markdown*") so pydantic marks that as required
Note: the Path param uses something like Path(..., description="description"), pydantic used to require ... as an argument to mark the field as a required, but this is not required in pydantic's newer versions, just omit that first default argument. See: https://stackoverflow.com/a/74884189
Related issues/PRs
Something similar has been done previously with the Path param https://github.com/OpenAPITools/openapi-generator/pull/17532, but this fix is not exactly the same (the parameter should not always be required as the path param, but rely on requestBody.required property to set the default value to None or not)
Suggest a fix
The problem is in this file:
https://github.com/OpenAPITools/openapi-generator/blob/f2813716fb815dae0e5bc6b2219452b3303f4934/modules/openapi-generator/src/main/resources/python-fastapi/endpoint_argument_definition.mustache
And in the definition of the Body parameter
When settings the first argument as None, we must check if the requestBody.required property is set to true, and in that case, omit the first argument entirely.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with modules/openapi-generator/src/main/resources/python-fastapi/endpoint_argument_definition.mustache and reproduce the issue using the provided OpenAPI declaration and generation command. Check the generated pets_post endpoint's Body argument against requestBody.required. Done means required bodies omit the None default while optional bodies retain their optional behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- fastapi, python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100