Skip to content

Commit

Permalink
config: add file tags header to config
Browse files Browse the repository at this point in the history
  • Loading branch information
switowski authored and egabancho committed Apr 16, 2018
1 parent 65ab29a commit f98dd17
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 47 deletions.
3 changes: 3 additions & 0 deletions invenio_files_rest/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,6 @@

FILES_REST_TASK_WAIT_MAX_SECONDS = 600
"""Maximum number of seconds to wait for a task to finish."""

FILES_REST_FILE_TAGS_HEADER = 'X-Invenio-File-Tags'
"""Header for updating file tags."""
19 changes: 8 additions & 11 deletions invenio_files_rest/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,12 @@ def load_or_import_from_config(key, app=None, default=None):
return obj_or_import_string(imp, default=default)


class QueryString(Field):
"""Field for deserializing query string-like headers."""
def deserialize_query_string(value):
"""Deserialize a query string into a dictionary.
def _deserialize(self, value, attr, data):
"""Deserialize a query string into a dictionary.
:returns: The deserialized dictionary.
"""
if not isinstance(value, six.string_types):
return None
return {key: (value[0] if len(value) > 0 else None) for key, value
in urlparse.parse_qs(value).items()}
:returns: The deserialized dictionary.
"""
if not isinstance(value, six.string_types):
return None
return {key: (value[0] if len(value) > 0 else None) for key, value
in urlparse.parse_qs(value).items()}
60 changes: 26 additions & 34 deletions invenio_files_rest/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from webargs import fields
from webargs.flaskparser import use_kwargs

from invenio_files_rest.utils import QueryString
from invenio_files_rest.utils import deserialize_query_string

from .errors import FileSizeError, MissingQueryParameter, \
MultipartInvalidChunkSize
Expand Down Expand Up @@ -104,27 +104,24 @@ def invalid_subresource_validator(value):
load_from='Content-MD5',
location='headers',
),
'x_invenio_file_tags': QueryString(
load_from='X-Invenio-File-Tags',
location='headers',
missing=None,
),
})
def default_partfactory(part_number=None, content_length=None,
content_type=None, content_md5=None,
x_invenio_file_tags=None):
content_type=None, content_md5=None):
"""Get default part factory.
:param part_number: The part number. (Default: ``None``)
:param content_length: The content length. (Default: ``None``)
:param content_type: The HTTP Content-Type. (Default: ``None``)
:param content_md5: The content MD5. (Default: ``None``)
:param x_invenio_file_tags: The file tags. (Default: ``None``)
:param file_tags_header: The file tags. (Default: ``None``)
:returns: The content length, the part number, the stream, the content
type, MD5 of the content.
"""
header = current_app.config['FILES_REST_FILE_TAGS_HEADER']
file_tags_header = deserialize_query_string(request.headers.get(header))

return content_length, part_number, request.stream, content_type, \
content_md5, x_invenio_file_tags
content_md5, file_tags_header


@use_kwargs({
Expand All @@ -144,27 +141,26 @@ def default_partfactory(part_number=None, content_length=None,
location='headers',
missing='',
),
'x_invenio_file_tags': QueryString(
load_from='X-Invenio-File-Tags',
location='headers',
missing=None,
),
})
def stream_uploadfactory(content_md5=None, content_length=None,
content_type=None, x_invenio_file_tags=None):
content_type=None):
"""Get default put factory.
If Content-Type is ``'multipart/form-data'`` then the stream is aborted.
:param content_md5: The content MD5. (Default: ``None``)
:param content_length: The content length. (Default: ``None``)
:param content_type: The HTTP Content-Type. (Default: ``None``)
:param x_invenio_file_tags: The file tags. (Default: ``None``)
:param file_tags_header: The file tags. (Default: ``None``)
:returns: The stream, content length, MD5 of the content.
"""
if content_type.startswith('multipart/form-data'):
abort(422)
return request.stream, content_length, content_md5, x_invenio_file_tags

header = current_app.config['FILES_REST_FILE_TAGS_HEADER']
file_tags_header = deserialize_query_string(request.headers.get(header))

return request.stream, content_length, content_md5, file_tags_header


@use_kwargs({
Expand All @@ -184,25 +180,23 @@ def stream_uploadfactory(content_md5=None, content_length=None,
location='files',
required=True,
),
'x_invenio_file_tags': QueryString(
load_from='X-Invenio-File-Tags',
location='headers',
missing=None,
),
})
def ngfileupload_partfactory(part_number=None, content_length=None,
uploaded_file=None, x_invenio_file_tags=None):
uploaded_file=None):
"""Part factory for ng-file-upload.
:param part_number: The part number. (Default: ``None``)
:param content_length: The content length. (Default: ``None``)
:param uploaded_file: The upload request. (Default: ``None``)
:param x_invenio_file_tags: The file tags. (Default: ``None``)
:param file_tags_header: The file tags. (Default: ``None``)
:returns: The content length, part number, stream, HTTP Content-Type
header.
"""
header = current_app.config['FILES_REST_FILE_TAGS_HEADER']
file_tags_header = deserialize_query_string(request.headers.get(header))

return content_length, part_number, uploaded_file.stream, \
uploaded_file.headers.get('Content-Type'), None, x_invenio_file_tags
uploaded_file.headers.get('Content-Type'), None, file_tags_header


@use_kwargs({
Expand All @@ -221,28 +215,26 @@ def ngfileupload_partfactory(part_number=None, content_length=None,
location='files',
required=True,
),
'x_invenio_file_tags': QueryString(
load_from='X-Invenio-File-Tags',
location='headers',
missing=None,
),
})
def ngfileupload_uploadfactory(content_length=None, content_type=None,
uploaded_file=None, x_invenio_file_tags=None):
uploaded_file=None):
"""Get default put factory.
If Content-Type is ``'multipart/form-data'`` then the stream is aborted.
:param content_length: The content length. (Default: ``None``)
:param content_type: The HTTP Content-Type. (Default: ``None``)
:param uploaded_file: The upload request. (Default: ``None``)
:param x_invenio_file_tags: The file tags. (Default: ``None``)
:param file_tags_header: The file tags. (Default: ``None``)
:returns: A tuple containing stream, content length, and empty header.
"""
if not content_type.startswith('multipart/form-data'):
abort(422)

return uploaded_file.stream, content_length, None, x_invenio_file_tags
header = current_app.config['FILES_REST_FILE_TAGS_HEADER']
file_tags_header = deserialize_query_string(request.headers.get(header))

return uploaded_file.stream, content_length, None, file_tags_header


#
Expand Down
5 changes: 3 additions & 2 deletions tests/test_views_objectversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,16 +526,17 @@ def test_delete_unwritable(client, db, bucket, versions):
FileInstance.query.count() == 4


def test_put_header_tags(client, bucket, permissions, get_md5, get_json):
def test_put_header_tags(app, client, bucket, permissions, get_md5, get_json):
"""Test upload of an object with tags in the headers."""

key = 'test.txt'
data = b'updated_content'
checksum = get_md5(data, prefix=True)
object_url = url_for(
'invenio_files_rest.object_api', bucket_id=bucket.id, key=key)
file_tags_header = app.config['FILES_REST_FILE_TAGS_HEADER']
headers = {
'X-Invenio-File-Tags': 'key1=val1;key2=val2;key3=val3;key1=invalid'
file_tags_header: 'key1=val1;key2=val2;key3=val3;key1=invalid'
}

login_user(client, permissions['bucket'])
Expand Down

0 comments on commit f98dd17

Please sign in to comment.