From 5de6dccfbb6a0ed39966dcff5038e7708c93d550 Mon Sep 17 00:00:00 2001 From: alfechner Date: Tue, 13 Feb 2024 23:01:10 +0100 Subject: [PATCH] Fix for JSON array parsing in multipart/form-data (#1869) (#1872) Fixes #1869 . Changes proposed in this pull request: - Prioritise parsing JSON over splitting array --------- Co-authored-by: Alex Fechner --- connexion/uri_parsing.py | 6 ++--- tests/fakeapi/hello/__init__.py | 9 +++++++ tests/fixtures/json_validation/openapi.yaml | 28 +++++++++++++++++++++ tests/test_json_validation.py | 24 ++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/connexion/uri_parsing.py b/connexion/uri_parsing.py index fb9c46468..0541ce29b 100644 --- a/connexion/uri_parsing.py +++ b/connexion/uri_parsing.py @@ -160,10 +160,10 @@ def resolve_form(self, form_data): form_data[k] = self._resolve_param_duplicates( form_data[k], encoding, "form" ) - if defn and defn["type"] == "array": - form_data[k] = self._split(form_data[k], encoding, "form") - elif "contentType" in encoding and all_json([encoding.get("contentType")]): + if "contentType" in encoding and all_json([encoding.get("contentType")]): form_data[k] = json.loads(form_data[k]) + elif defn and defn["type"] == "array": + form_data[k] = self._split(form_data[k], encoding, "form") form_data[k] = coerce_type(defn, form_data[k], "requestBody", k) return form_data diff --git a/tests/fakeapi/hello/__init__.py b/tests/fakeapi/hello/__init__.py index dee31f0af..de2af8fdd 100644 --- a/tests/fakeapi/hello/__init__.py +++ b/tests/fakeapi/hello/__init__.py @@ -650,6 +650,15 @@ def post_multipart_form(body): return x +def post_multipart_form_array(body): + result = [] + for x in body["x"]: + x["name"] += "-reply" + x["age"] += 10 + result.append(x) + return result + + def apikey_info(apikey, required_scopes=None): if apikey == "mykey": return {"sub": "admin"} diff --git a/tests/fixtures/json_validation/openapi.yaml b/tests/fixtures/json_validation/openapi.yaml index 9c45d5f13..c336eb0b3 100644 --- a/tests/fixtures/json_validation/openapi.yaml +++ b/tests/fixtures/json_validation/openapi.yaml @@ -117,3 +117,31 @@ paths: application/json: schema: $ref: "#/components/schemas/X" + + + /multipart_form_json_array: + post: + operationId: fakeapi.hello.post_multipart_form_array + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + properties: + x: + type: array + items: + $ref: "#/components/schemas/X" + encoding: + x: + contentType: "application/json" + responses: + 200: + description: Modified Echo + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/X" \ No newline at end of file diff --git a/tests/test_json_validation.py b/tests/test_json_validation.py index ba389b984..df613e193 100644 --- a/tests/test_json_validation.py +++ b/tests/test_json_validation.py @@ -128,3 +128,27 @@ def test_multipart_form_json(json_validation_spec_dir, spec, app_class): assert res.status_code == 200 assert res.json()["name"] == "joe-reply" assert res.json()["age"] == 30 + + +@pytest.mark.parametrize("spec", ["openapi.yaml"]) +def test_multipart_form_json_array(json_validation_spec_dir, spec, app_class): + app = build_app_from_fixture( + json_validation_spec_dir, + app_class=app_class, + spec_file=spec, + validate_responses=True, + ) + app_client = app.test_client() + + res = app_client.post( + "/v1.0/multipart_form_json_array", + files={"file": b""}, # Force multipart/form-data content-type + data={ + "x": json.dumps([{"name": "joe", "age": 20}, {"name": "alena", "age": 28}]) + }, + ) + assert res.status_code == 200 + assert res.json()[0]["name"] == "joe-reply" + assert res.json()[0]["age"] == 30 + assert res.json()[1]["name"] == "alena-reply" + assert res.json()[1]["age"] == 38