From 6185a92aeec7c4d7dd6d2f4588f3737cc333a372 Mon Sep 17 00:00:00 2001 From: "nash.jung" Date: Wed, 19 Apr 2023 21:25:28 +0900 Subject: [PATCH] fix: get field_info per param --- fastapi/dependencies/utils.py | 1 + tests/test_request_form_file.py | 179 ++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 tests/test_request_form_file.py diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index f131001ce2..ec24c04f54 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -725,6 +725,7 @@ async def request_body_to_args( received_body = {field.alias: received_body} for field in required_params: + field_info = field.field_info loc: Tuple[str, ...] if field_alias_omitted: loc = ("body",) diff --git a/tests/test_request_form_file.py b/tests/test_request_form_file.py new file mode 100644 index 0000000000..52088bfb9f --- /dev/null +++ b/tests/test_request_form_file.py @@ -0,0 +1,179 @@ +from typing import Annotated + +from fastapi import FastAPI, File, Form +from fastapi.testclient import TestClient + +app = FastAPI() + + +@app.post("/files/") +async def create_file(token: Annotated[str, Form()], file: Annotated[bytes, File()]): + return { + "file_size": len(file), + "token": token, + } + + +client = TestClient(app) + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/files/": { + "post": { + "summary": "Create File", + "operationId": "create_file_files__post", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_create_file_files__post" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + } + }, + "components": { + "schemas": { + "Body_create_file_files__post": { + "title": "Body_create_file_files__post", + "required": ["token", "file"], + "type": "object", + "properties": { + "token": {"title": "Token", "type": "string"}, + "file": {"title": "File", "type": "string", "format": "binary"}, + }, + }, + "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": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, +} + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == openapi_schema + + +file_required = { + "detail": [ + { + "loc": ["body", "file"], + "msg": "field required", + "type": "value_error.missing", + }, + ] +} + +token_required = { + "detail": [ + { + "loc": ["body", "token"], + "msg": "field required", + "type": "value_error.missing", + }, + ] +} + +file_and_token_required = { + "detail": [ + { + "loc": ["body", "token"], + "msg": "field required", + "type": "value_error.missing", + }, + { + "loc": ["body", "file"], + "msg": "field required", + "type": "value_error.missing", + }, + ] +} + + +def test_post_form_no_body(): + response = client.post("/files/") + assert response.status_code == 422, response.text + assert response.json() == file_and_token_required + + +def test_post_form_no_file(): + response = client.post("/files/", data={"token": "foo"}) + assert response.status_code == 422, response.text + assert response.json() == file_required + + +def test_post_body_json(): + response = client.post("/files/", json={"file": "Foo", "token": "Bar"}) + assert response.status_code == 422, response.text + assert response.json() == file_and_token_required + + +def test_post_file_no_token(tmp_path): + path = tmp_path / "test.txt" + path.write_text("") + with path.open("rb") as file: + response = client.post( + "/files/", + files={"file": file}, + ) + assert response.status_code == 422, response.text + assert response.json() == token_required + + +def test_post_files_and_token(tmp_path): + path = tmp_path / "test.txt" + path.write_text("") + with path.open("rb") as file: + response = client.post( + "/files/", + data={"token": "foo"}, + files={"file": file}, + ) + assert response.status_code == 200, response.text + assert response.json() == {"file_size": 14, "token": "foo"}