Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File support #145

Merged
merged 29 commits into from
Oct 14, 2020
Merged
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
68585b3
add file utils
leahein Jul 12, 2020
aabd2de
initial add for file support
leahein Jul 12, 2020
f6ffdd3
Implement file handling in aiohttp.py
quasimik Jul 16, 2020
e637983
Fix JSON serialization, remove comments, conform to double quotes
quasimik Jul 31, 2020
9e86b03
Fix more JSON serialization
quasimik Jul 31, 2020
c2e38dc
Cleanup
quasimik Jul 31, 2020
e6ae391
FF, merge
quasimik Jul 31, 2020
8839141
Blackened
quasimik Jul 31, 2020
194d645
fix: safe check if parameters are none on aiohttp
KingDarBoja Sep 19, 2020
e02399a
Merge branch 'master' into file-support
KingDarBoja Sep 19, 2020
2a020ad
chore: generate docs with sphinx (#117)
KingDarBoja Sep 20, 2020
25c243a
GitHub Actions: do the tests for each push
leszekhanusz Sep 27, 2020
40f2aaf
Tests: add pypy3 tests again
leszekhanusz Sep 27, 2020
57bb4aa
GitHub Actions: try to send coverage to coveralls.io
leszekhanusz Sep 27, 2020
c2f1840
GitHub actions: try to send coverage to coveralls.io (2)
leszekhanusz Sep 27, 2020
1ba67a7
GitHub Actions: migrating from coveralls to codecov
leszekhanusz Sep 27, 2020
e72bd6b
GitHub Actions: fix typo
leszekhanusz Sep 27, 2020
9e6dc7e
README.md fix badges, add link to doc and leave only basic example (#…
leszekhanusz Sep 27, 2020
53c7a32
Single-sourcing the version in a __version__.py file (#142)
leszekhanusz Sep 27, 2020
4d11c89
Bump version number
leszekhanusz Sep 27, 2020
eec9220
Only upload files if the upload_files flag is True
leszekhanusz Oct 3, 2020
f647803
Adding tests for the file upload functionality
leszekhanusz Oct 3, 2020
62c6a58
Add docs
leszekhanusz Oct 3, 2020
cda8263
Merge branch 'master' into file-support
leszekhanusz Oct 6, 2020
27a01c7
Merge branch 'master' into file-support
leszekhanusz Oct 6, 2020
e488612
fix file upload tests on windows and add a binary file upload test
leszekhanusz Oct 10, 2020
e92eee2
fix mypy
leszekhanusz Oct 10, 2020
19e4e25
Merge branch 'master' into file-support
leszekhanusz Oct 14, 2020
a96ed24
Merge branch 'master' into file-support
leszekhanusz Oct 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Adding tests for the file upload functionality
leszekhanusz committed Oct 3, 2020
commit f647803d0ee59c0d273d2a132cb6539fd6b0e641
22 changes: 22 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import os
import pathlib
import ssl
import tempfile
import types
from concurrent.futures import ThreadPoolExecutor

@@ -187,6 +188,27 @@ async def send_connection_ack(ws):
await ws.send('{"event":"phx_reply", "payload": {"status": "ok"}, "ref": 1}')


class TemporaryFile:
"""Class used to generate temporary files for the tests"""

def __init__(self, content):

self.file = tempfile.NamedTemporaryFile(mode="w", delete=False)

with self.file as f:
f.write(content)

@property
def filename(self):
return self.file.name

def __enter__(self):
return self

def __exit__(self, type, value, traceback):
os.unlink(self.filename)


def get_server_handler(request):
"""Get the server handler.

298 changes: 298 additions & 0 deletions tests/test_aiohttp.py
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@
TransportServerError,
)

from .conftest import TemporaryFile

query1_str = """
query getContinents {
continents {
@@ -321,3 +323,299 @@ def test_code():
pass

await run_sync_test(event_loop, server, test_code)


file_upload_server_answer = '{"data":{"success":true}}'

file_upload_mutation_1 = """
mutation($file: Upload!) {
uploadFile(input:{other_var:$other_var, file:$file}) {
success
}
}
"""

file_upload_mutation_1_operations = (
'{"query": "mutation ($file: Upload!) {\\n uploadFile(input: {other_var: '
'$other_var, file: $file}) {\\n success\\n }\\n}\\n", "variables": '
'{"file": null, "other_var": 42}}'
)

file_upload_mutation_1_map = '{"0": ["variables.file"]}'

file_1_content = """
This is a test file
This file will be sent in the GraphQL mutation
"""


async def single_upload_handler(request):

reader = await request.multipart()

field_0 = await reader.next()
assert field_0.name == "operations"
field_0_text = await field_0.text()
assert field_0_text == file_upload_mutation_1_operations

field_1 = await reader.next()
assert field_1.name == "map"
field_1_text = await field_1.text()
assert field_1_text == file_upload_mutation_1_map

field_2 = await reader.next()
assert field_2.name == "0"
field_2_text = await field_2.text()
assert field_2_text == file_1_content

field_3 = await reader.next()
assert field_3 is None

return web.Response(text=file_upload_server_answer, content_type="application/json")


@pytest.mark.asyncio
async def test_aiohttp_file_upload(event_loop, aiohttp_server):
app = web.Application()
app.router.add_route("POST", "/", single_upload_handler)
server = await aiohttp_server(app)

url = server.make_url("/")

sample_transport = AIOHTTPTransport(url=url, timeout=10)

with TemporaryFile(file_1_content) as test_file:

async with Client(transport=sample_transport,) as session:

query = gql(file_upload_mutation_1)

file_path = test_file.filename

with open(file_path, "rb") as f:

params = {"file": f, "other_var": 42}

# Execute query asynchronously
result = await session.execute(
query, variable_values=params, upload_files=True
)

success = result["success"]

assert success


@pytest.mark.asyncio
async def test_aiohttp_file_upload_without_session(
event_loop, aiohttp_server, run_sync_test
):

app = web.Application()
app.router.add_route("POST", "/", single_upload_handler)
server = await aiohttp_server(app)

url = server.make_url("/")

def test_code():
sample_transport = AIOHTTPTransport(url=url, timeout=10)

with TemporaryFile(file_1_content) as test_file:

client = Client(transport=sample_transport,)

query = gql(file_upload_mutation_1)

file_path = test_file.filename

with open(file_path, "rb") as f:

params = {"file": f, "other_var": 42}

result = client.execute(
query, variable_values=params, upload_files=True
)

success = result["success"]

assert success

await run_sync_test(event_loop, server, test_code)


file_upload_mutation_2 = """
mutation($file1: Upload!, $file2: Upload!) {
uploadFile(input:{file1:$file, file2:$file}) {
success
}
}
"""

file_upload_mutation_2_operations = (
'{"query": "mutation ($file1: Upload!, $file2: Upload!) {\\n '
'uploadFile(input: {file1: $file, file2: $file}) {\\n success\\n }\\n}\\n", '
'"variables": {"file1": null, "file2": null}}'
)

file_upload_mutation_2_map = '{"0": ["variables.file1"], "1": ["variables.file2"]}'

file_2_content = """
This is a second test file
This file will also be sent in the GraphQL mutation
"""


@pytest.mark.asyncio
async def test_aiohttp_file_upload_two_files(event_loop, aiohttp_server):
async def handler(request):

reader = await request.multipart()

field_0 = await reader.next()
assert field_0.name == "operations"
field_0_text = await field_0.text()
assert field_0_text == file_upload_mutation_2_operations

field_1 = await reader.next()
assert field_1.name == "map"
field_1_text = await field_1.text()
assert field_1_text == file_upload_mutation_2_map

field_2 = await reader.next()
assert field_2.name == "0"
field_2_text = await field_2.text()
assert field_2_text == file_1_content

field_3 = await reader.next()
assert field_3.name == "1"
field_3_text = await field_3.text()
assert field_3_text == file_2_content

field_4 = await reader.next()
assert field_4 is None

return web.Response(
text=file_upload_server_answer, content_type="application/json"
)

app = web.Application()
app.router.add_route("POST", "/", handler)
server = await aiohttp_server(app)

url = server.make_url("/")

sample_transport = AIOHTTPTransport(url=url, timeout=10)

with TemporaryFile(file_1_content) as test_file_1:
with TemporaryFile(file_2_content) as test_file_2:

async with Client(transport=sample_transport,) as session:

query = gql(file_upload_mutation_2)

file_path_1 = test_file_1.filename
file_path_2 = test_file_2.filename

f1 = open(file_path_1, "rb")
f2 = open(file_path_2, "rb")

params = {
"file1": f1,
"file2": f2,
}

result = await session.execute(
query, variable_values=params, upload_files=True
)

f1.close()
f2.close()

success = result["success"]

assert success


file_upload_mutation_3 = """
mutation($files: [Upload!]!) {
uploadFiles(input:{files:$files}) {
success
}
}
"""

file_upload_mutation_3_operations = (
'{"query": "mutation ($files: [Upload!]!) {\\n uploadFiles(input: {files: $files})'
' {\\n success\\n }\\n}\\n", "variables": {"files": [null, null]}}'
)

file_upload_mutation_3_map = '{"0": ["variables.files.0"], "1": ["variables.files.1"]}'


@pytest.mark.asyncio
async def test_aiohttp_file_upload_list_of_two_files(event_loop, aiohttp_server):
async def handler(request):

reader = await request.multipart()

field_0 = await reader.next()
assert field_0.name == "operations"
field_0_text = await field_0.text()
assert field_0_text == file_upload_mutation_3_operations

field_1 = await reader.next()
assert field_1.name == "map"
field_1_text = await field_1.text()
assert field_1_text == file_upload_mutation_3_map

field_2 = await reader.next()
assert field_2.name == "0"
field_2_text = await field_2.text()
assert field_2_text == file_1_content

field_3 = await reader.next()
assert field_3.name == "1"
field_3_text = await field_3.text()
assert field_3_text == file_2_content

field_4 = await reader.next()
assert field_4 is None

return web.Response(
text=file_upload_server_answer, content_type="application/json"
)

app = web.Application()
app.router.add_route("POST", "/", handler)
server = await aiohttp_server(app)

url = server.make_url("/")

sample_transport = AIOHTTPTransport(url=url, timeout=10)

with TemporaryFile(file_1_content) as test_file_1:
with TemporaryFile(file_2_content) as test_file_2:

async with Client(transport=sample_transport,) as session:

query = gql(file_upload_mutation_3)

file_path_1 = test_file_1.filename
file_path_2 = test_file_2.filename

f1 = open(file_path_1, "rb")
f2 = open(file_path_2, "rb")

params = {"files": [f1, f2]}

# Execute query asynchronously
result = await session.execute(
query, variable_values=params, upload_files=True
)

f1.close()
f2.close()

success = result["success"]

assert success