From 23ef44029854709dff7fbe912b36ada9db5d2c84 Mon Sep 17 00:00:00 2001 From: Ori Levari Date: Fri, 10 Dec 2021 18:33:29 -0800 Subject: [PATCH 1/2] modify deep_dict_update to merge rather than overwrite arrays. This allows query parameters to be specified in openapi_extras not be appended to the spec, not overwrite. --- fastapi/utils.py | 5 + .../test_openapi_query_parameter_extension.py | 130 ++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 tests/test_openapi_query_parameter_extension.py diff --git a/fastapi/utils.py b/fastapi/utils.py index 8913d85b2dc40..74179c7bdfa7a 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -134,6 +134,11 @@ def deep_dict_update(main_dict: Dict[Any, Any], update_dict: Dict[Any, Any]) -> and isinstance(update_dict[key], dict) ): deep_dict_update(main_dict[key], update_dict[key]) + elif (key in main_dict + and isinstance(main_dict[key], list) + and isinstance(update_dict[key], list) + ): + main_dict[key] = main_dict[key] + update_dict[key] else: main_dict[key] = update_dict[key] diff --git a/tests/test_openapi_query_parameter_extension.py b/tests/test_openapi_query_parameter_extension.py new file mode 100644 index 0000000000000..c799f465d660d --- /dev/null +++ b/tests/test_openapi_query_parameter_extension.py @@ -0,0 +1,130 @@ +from typing import Optional + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +app = FastAPI() + + +@app.get( + "/", + openapi_extra={ + "parameters": [ + { + "required": False, + "schema": {"title": "Extra Param 1"}, + "name": "extra_param_1", + "in": "query", + }, + { + "required": True, + "schema": {"title": "Extra Param 2"}, + "name": "extra_param_2", + "in": "query", + }, + ] + }, +) +def route_with_extra_query_parameters(standard_query_param: Optional[int] = 50): + return {} + + +client = TestClient(app) + + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/": { + "get": { + "summary": "Route With Extra Query Parameters", + "operationId": "route_with_extra_query_parameters__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Standard Query Param", + "type": "integer", + "default": 50 + }, + "name": "standard_query_param", + "in": "query" + }, + { + "required": False, + "schema": {"title": "Extra Param 1"}, + "name": "extra_param_1", + "in": "query", + }, + { + "required": True, + "schema": {"title": "Extra Param 2"}, + "name": "extra_param_2", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": {"type": "string"}, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, +} + + +def test_openapi(): + response = client.get("/openapi.json") + print(response.json()) + print() + print(openapi_schema) + assert response.status_code == 200, response.text + assert response.json() == openapi_schema + + +def test_get_route(): + response = client.get("/") + assert response.status_code == 200, response.text + assert response.json() == {} From 7c8e75aaee76cc0748ba0c310cfc9425652484b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 26 Aug 2022 11:22:32 +0200 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=85=20Update=20tests=20for=20extra=20?= =?UTF-8?q?OpenAPI=20parameters=20with=20lists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_openapi_query_parameter_extension.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/test_openapi_query_parameter_extension.py b/tests/test_openapi_query_parameter_extension.py index c799f465d660d..d3996f26ee417 100644 --- a/tests/test_openapi_query_parameter_extension.py +++ b/tests/test_openapi_query_parameter_extension.py @@ -46,10 +46,10 @@ def route_with_extra_query_parameters(standard_query_param: Optional[int] = 50): "schema": { "title": "Standard Query Param", "type": "integer", - "default": 50 + "default": 50, }, "name": "standard_query_param", - "in": "query" + "in": "query", }, { "required": False, @@ -104,7 +104,7 @@ def route_with_extra_query_parameters(standard_query_param: Optional[int] = 50): "loc": { "title": "Location", "type": "array", - "items": {"type": "string"}, + "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, }, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}, @@ -117,9 +117,6 @@ def route_with_extra_query_parameters(standard_query_param: Optional[int] = 50): def test_openapi(): response = client.get("/openapi.json") - print(response.json()) - print() - print(openapi_schema) assert response.status_code == 200, response.text assert response.json() == openapi_schema