-
-
Notifications
You must be signed in to change notification settings - Fork 755
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix for aiohttp and multipart/form-data uploads (#1222)
* Added unit tests to demonstrate the problems of #975 - Taken mostly from existing PR: #987 * now splitting out multipart POSTs into files[] and form[], handling duplicate keys as the rest of connexion expects - Based parly on existing PR: #987 * rewrote how operations/openapi.py::_get_body_argument() works to better build the arguments[] list according to what the spec says and what the handler accepts. This fixes a bug when requests contain mixed files and form values and the handler is expecting variable names matching the request property names. * Adding unit tests to improve code converage test * post merge fixes - using 'async' keyword now in new unit test file * unit test improvements -- now testing the contents of the files we upload too * making some code a bit clearer regarding duplicate names of file submissions * fixing up unit tests since merging main * fixing isort-check-tests and flake8 * clarified a comment * comment correction * after discussions with maintainer, reverted _get_body_argument back to the original where it does not attempt to break out the body into individual arguments for the handler. But left in changes that make the normal behavior of not passing a body argument to a handler without one more consistent when the body itself is empty or not an object type. * fixing unit tests after after reverting _get_body_argument behavior
- Loading branch information
Showing
9 changed files
with
449 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import os | ||
from pathlib import Path | ||
|
||
import pytest | ||
from connexion import AioHttpApp | ||
|
||
import aiohttp | ||
|
||
try: | ||
import ujson as json | ||
except ImportError: | ||
import json | ||
|
||
|
||
@pytest.fixture | ||
def aiohttp_app(aiohttp_api_spec_dir): | ||
app = AioHttpApp(__name__, port=5001, | ||
specification_dir=aiohttp_api_spec_dir, | ||
debug=True) | ||
app.add_api( | ||
'openapi_multipart.yaml', | ||
validate_responses=True, | ||
strict_validation=True, | ||
pythonic_params=True, | ||
pass_context_arg_name='request_ctx', | ||
) | ||
return app | ||
|
||
|
||
async def test_single_file_upload(aiohttp_app, aiohttp_client): | ||
app_client = await aiohttp_client(aiohttp_app.app) | ||
|
||
resp = await app_client.post( | ||
'/v1.0/upload_file', | ||
data=aiohttp.FormData(fields=[('myfile', open(__file__, 'rb'))])(), | ||
) | ||
|
||
data = await resp.json() | ||
assert resp.status == 200 | ||
assert data['fileName'] == f'{__name__}.py' | ||
assert data['myfile_content'] == Path(__file__).read_bytes().decode('utf8') | ||
|
||
|
||
async def test_many_files_upload(aiohttp_app, aiohttp_client): | ||
app_client = await aiohttp_client(aiohttp_app.app) | ||
|
||
dir_name = os.path.dirname(__file__) | ||
files_field = [ | ||
('myfiles', open(f'{dir_name}/{file_name}', 'rb')) \ | ||
for file_name in sorted(os.listdir(dir_name)) if file_name.endswith('.py') | ||
] | ||
|
||
form_data = aiohttp.FormData(fields=files_field) | ||
|
||
resp = await app_client.post( | ||
'/v1.0/upload_files', | ||
data=form_data(), | ||
) | ||
|
||
data = await resp.json() | ||
|
||
assert resp.status == 200 | ||
assert data['files_count'] == len(files_field) | ||
assert data['myfiles_content'] == [ | ||
Path(f'{dir_name}/{file_name}').read_bytes().decode('utf8') \ | ||
for file_name in sorted(os.listdir(dir_name)) if file_name.endswith('.py') | ||
] | ||
|
||
|
||
async def test_mixed_multipart_single_file(aiohttp_app, aiohttp_client): | ||
app_client = await aiohttp_client(aiohttp_app.app) | ||
|
||
form_data = aiohttp.FormData() | ||
form_data.add_field('dir_name', os.path.dirname(__file__)) | ||
form_data.add_field('myfile', open(__file__, 'rb')) | ||
|
||
resp = await app_client.post( | ||
'/v1.0/mixed_single_file', | ||
data=form_data(), | ||
) | ||
|
||
data = await resp.json() | ||
|
||
assert resp.status == 200 | ||
assert data['dir_name'] == os.path.dirname(__file__) | ||
assert data['fileName'] == f'{__name__}.py' | ||
assert data['myfile_content'] == Path(__file__).read_bytes().decode('utf8') | ||
|
||
|
||
|
||
async def test_mixed_multipart_many_files(aiohttp_app, aiohttp_client): | ||
app_client = await aiohttp_client(aiohttp_app.app) | ||
|
||
dir_name = os.path.dirname(__file__) | ||
files_field = [ | ||
('myfiles', open(f'{dir_name}/{file_name}', 'rb')) \ | ||
for file_name in sorted(os.listdir(dir_name)) if file_name.endswith('.py') | ||
] | ||
|
||
form_data = aiohttp.FormData(fields=files_field) | ||
form_data.add_field('dir_name', os.path.dirname(__file__)) | ||
form_data.add_field('test_count', str(len(files_field))) | ||
|
||
resp = await app_client.post( | ||
'/v1.0/mixed_many_files', | ||
data=form_data(), | ||
) | ||
|
||
data = await resp.json() | ||
|
||
assert resp.status == 200 | ||
assert data['dir_name'] == os.path.dirname(__file__) | ||
assert data['test_count'] == len(files_field) | ||
assert data['files_count'] == len(files_field) | ||
assert data['myfiles_content'] == [ | ||
Path(f'{dir_name}/{file_name}').read_bytes().decode('utf8') \ | ||
for file_name in sorted(os.listdir(dir_name)) if file_name.endswith('.py') | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.