diff --git a/fastapi/applications.py b/fastapi/applications.py index 05c7bd2be..e35dd8548 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -810,6 +810,8 @@ class FastAPI(Starlette): """ ), ] = True, + openapi_schema_exclude_unset: bool = False, + openapi_schema_exclude_none: bool = True, **extra: Annotated[ Any, Doc( @@ -871,6 +873,8 @@ class FastAPI(Starlette): ), ] = "3.1.0" self.openapi_schema: Optional[Dict[str, Any]] = None + self.openapi_schema_exclude_unset = openapi_schema_exclude_unset + self.openapi_schema_exclude_none = openapi_schema_exclude_none if self.openapi_url: assert self.title, "A title must be provided for OpenAPI, e.g.: 'My API'" assert self.version, "A version must be provided for OpenAPI, e.g.: '2.1.0'" @@ -992,6 +996,8 @@ class FastAPI(Starlette): tags=self.openapi_tags, servers=self.servers, separate_input_output_schemas=self.separate_input_output_schemas, + exclude_unset=self.openapi_schema_exclude_unset, + exclude_none=self.openapi_schema_exclude_none, ) return self.openapi_schema diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 808646cc2..1e35d3814 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -489,6 +489,8 @@ def get_openapi( contact: Optional[Dict[str, Union[str, Any]]] = None, license_info: Optional[Dict[str, Union[str, Any]]] = None, separate_input_output_schemas: bool = True, + exclude_unset: bool = False, + exclude_none: bool = True, ) -> Dict[str, Any]: info: Dict[str, Any] = {"title": title, "version": version} if summary: @@ -566,4 +568,9 @@ def get_openapi( output["webhooks"] = webhook_paths if tags: output["tags"] = tags - return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore + return jsonable_encoder( # type: ignore + OpenAPI(**output), + by_alias=True, + exclude_unset=exclude_unset, + exclude_none=exclude_none, + ) diff --git a/tests/test_get_openapi_schema.py b/tests/test_get_openapi_schema.py new file mode 100644 index 000000000..05fa8d2a0 --- /dev/null +++ b/tests/test_get_openapi_schema.py @@ -0,0 +1,57 @@ +from fastapi import FastAPI +from fastapi.testclient import TestClient + +app = FastAPI( + openapi_schema_exclude_unset=True, + openapi_schema_exclude_none=False, +) + + +@app.get( + "/items", + responses={ + 200: {"content": {"application/json": {"example": {"id": None, "value": 50}}}} + }, +) +def get_items(): + return {"id": "foo", "value": 50} + + +client = TestClient(app) + + +openapi_schema = { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {}, + "example": {"id": None, "value": 50}, + } + }, + } + }, + "summary": "Get Items", + "operationId": "get_items_items_get", + } + } + }, +} + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == openapi_schema + + +def test_default_get_items(): + response = client.get("/items") + assert response.status_code == 200, response.text + assert response.json() == {"id": "foo", "value": 50}