graphql-python/gql

GraphQL multipart uploads do not contain mimetype

aalmazan opened this issue · 3 comments

Describe the bug
Currently on v3.4.0 when uploading files, a mimetype is not being provided. This seems to default files as text/plain, at least on the Apollo GraphQL server implementation I've testing against.

The transport I'm currently using is requests, however, it's probably the case on other transports as well:

https://github.com/graphql-python/gql/blob/master/gql/transport/requests.py#L185-L187
https://github.com/graphql-python/gql/blob/master/gql/transport/aiohttp.py#L276-L278

Changing the following will work for my use case (requests transport):

# original
fields[k] = (getattr(v, "name", k), v)
# new 
fields[k] = (getattr(v, "name", k), v, 'application/pdf')
# or 
fields[k] = (getattr(v, "name", k), v, USER_PROVIDED_MIMETYPE)

To Reproduce
Same as above

Expected behavior

I suspect there's two ways about this:

  1. Autodetect the file's mimetype (or have an argument somewhere that allows the user to opt-in to this behavior):
import mimetypes
mimetype, _ = mimetypes.guess_type(file.name)
  1. Allow the user to provide the mimetype manually:
# Taken from the docs on uploading files: https://gql.readthedocs.io/en/stable/usage/file_upload.html
# Single file upload
with open("YOUR_FILE_PATH", "rb") as f:
    # old
    # params = {"file": f}
    # new
    params = {"file": (f, USER_DEFINED_MIMETYPE)}
    result = client.execute(
        query, variable_values=params, upload_files=True
    )


# Multi-file upload
f1 = open("YOUR_FILE_PATH_1", "rb")
f2 = open("YOUR_FILE_PATH_2", "rb")

params = {"files": [(f1, USER_DEFINED_MIMETYPE_1), (f2, USER_DEFINED_MIMETYPE_2)]}

System info (please complete the following information):

  • OS: Ubuntu
  • Python version: 3.9.12
  • gql version: 3.4.0
  • graphql-core version:

Option 1. could be done.

Option 2. should be done another way as we are detecting the files by checking their type, and now the provided value is a tuple instead of a File object.

One way to do it would be to add a new parameter to the file object and use it if it is present.
Something like this:

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

    f.content_type = "application/pdf";

    params = {"file": f}
    result = client.execute(
        query, variable_values=params, upload_files=True
    )

Please check the PR #386

Only the option 2 has been retained.
Detecting the content-type depending on the filename can be done by the user if needed.

@leszekhanusz Thanks for the quick PR! I created a fork that had very similar changes as well (but just for requests) and it all works well for me.