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

Add helper method to support file uploads #49

Open
paul121 opened this issue Jul 2, 2021 · 0 comments
Open

Add helper method to support file uploads #49

paul121 opened this issue Jul 2, 2021 · 0 comments

Comments

@paul121
Copy link
Member

paul121 commented Jul 2, 2021

Some context on this:

An overview:

JSON:API now:

Supports POSTing a file's contents
Supports sending file data as a binary data stream via the Content-Type: application/octet-stream request header.
This allows uploads of an arbitrary size, including uploads larger than the PHP memory limit.
Ensures that settings/validation constraints of the field to which the file is being uploaded will be respected.
Supports naming the file using the Content-Disposition: file; filename="filename.jpg" header

To use JSON:API's file uploads, you must choose to implement one or both of two different "flows":

Flow 1 requires only a single HTTP request, but will only work when the host entity already exists.
Flow 2 requires two HTTP requests but will work under all circumstances.

TL;DR

I tested uploading files using the requests session from the farmOS.py client and it works great:

client.session.post('http://localhost/api/log/input/file', data=open('data.csv', 'rb').read(), headers={'Content-Type': 'application/octet-stream', 'Content-Disposition': 'file; filename="data.csv"'})

We can make this even easier in the farmOS.py client by adding a helper method. This can't be done with the existing methods because file uploads must always be a POST with special headers to unique {entity_type}/{bundle}/{file field} endpoints. This ensures proper files are uploaded to the correct location (the location can vary depending on each bundle field) and all proper validation is performed.

Solution:

# Method to be namespaced as client.file.create()
def create(entity_type, bundle, field, data, filename, id = None):
    if id is not None:
        path = "{entity_type}/{bundle}/{id}/{field}".format(entity_type, bundle, id, field)
    else:
        path = "{entity_type}/{bundle}/{field}".format(entity_type, bundle, field)
    headers = {'Content-Type': 'application/octet-stream', 'Content-Disposition': "file; filename=\"{filename}\"".format(filename)} 
    return self.session.post(path, data, headers)
    
# Usage

# Flow 1: update existing log
data = open('data.csv', 'rb').read()
response = client.file.create('log', 'observation', 'file', data, 'observation_data.csv', '{log_id}')

# Flow 2: upload file then include with a new log.
data = open('data.csv', 'rb').read()
response = client.file.create('log', 'observation', 'file', data, 'observation_data.csv')
file_id = response['data']['id']
log_data = {
  'attributes': {'name': 'observation log'},
  'relationships': { 'file': {'data': [{'type': 'file--file', 'id': file_id}]}},
}
new_log = client.log.send('observation', log_data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant