diff --git a/api/placer.py b/api/placer.py index dc16c4482756d4392541ab2d6440e5e24a5c7f25..9b454828e9cf1d2f76a08ca51544f4c708267530 100644 --- a/api/placer.py +++ b/api/placer.py @@ -18,6 +18,7 @@ from .jobs import rules from .jobs.jobs import Job from .types import Origin from .web import encoder +from .web.errors import FileFormException class Placer(object): """ @@ -74,7 +75,7 @@ class Placer(object): Helper function that throws unless metadata was provided. """ if self.metadata == None: - raise Exception('Metadata required') + raise FileFormException('Metadata required') def save_file(self, field=None, file_attrs=None): """ @@ -140,7 +141,7 @@ class UIDPlacer(Placer): super(UIDPlacer, self).__init__(container_type, container, id_, metadata, timestamp, origin, context) self.metadata_for_file = {} self.session_id = None - + self.count = 0 def check(self): self.requireMetadata() @@ -149,22 +150,25 @@ class UIDPlacer(Placer): metadata_validator = validators.from_schema_path(payload_schema_uri) metadata_validator(self.metadata, 'POST') - # If not a superuser request, pass uid of user making the upload request - targets = self.create_hierarchy(self.metadata, type_=self.match_type, user=self.context.get('uid')) - - self.metadata_for_file = {} - - for target in targets: - if target[0].level is 'session': - self.session_id = target[0].id_ - for name in target[1]: - self.metadata_for_file[name] = { - 'container': target[0], - 'metadata': target[1][name] - } def process_file_field(self, field, file_attrs): + # Only create the hierarchy once + if self.count == 0: + # If not a superuser request, pass uid of user making the upload request + targets = self.create_hierarchy(self.metadata, type_=self.match_type, user=self.context.get('uid')) + + self.metadata_for_file = {} + + for target in targets: + if target[0].level is 'session': + self.session_id = target[0].id_ + for name in target[1]: + self.metadata_for_file[name] = { + 'container': target[0], + 'metadata': target[1][name] + } + self.count += 1 # For the file, given self.targets, choose a target name = field.filename @@ -194,6 +198,9 @@ class UIDPlacer(Placer): self.saved.append(file_attrs) def finalize(self): + # Check that there is at least one file being uploaded + if self.count < 1: + raise FileFormException("No files selected for upload") if self.session_id: self.container_type = 'session' self.id_ = self.session_id diff --git a/api/upload.py b/api/upload.py index 35db758b50b1f8d7031561b93dd43574faebb8ab..12e051da4970aaf7050cb8824896863bdfa16425 100644 --- a/api/upload.py +++ b/api/upload.py @@ -5,7 +5,7 @@ import os.path import shutil from .web import base -from .web.errors import FileStoreException +from .web.errors import FileStoreException, FileFormException from . import config from . import files from . import placer as pl @@ -95,7 +95,8 @@ def process_upload(request, strategy, container_type=None, id_=None, origin=None # TODO: Change schemas to enabled targeted uploads of more than one file. # Ref docs from placer.TargetedPlacer for details. if strategy == Strategy.targeted and len(file_fields) > 1: - raise Exception("Targeted uploads can only send one file") + raise FileFormException("Targeted uploads can only send one file") + for field in file_fields: diff --git a/api/web/base.py b/api/web/base.py index c9deb3886b22fb6c96f3c76782fdbbf7c418fbfc..068b90ebddb97b0f3553ec063e7f0d42acc2f835 100644 --- a/api/web/base.py +++ b/api/web/base.py @@ -358,6 +358,8 @@ class RequestHandler(webapp2.RequestHandler): code = 400 elif isinstance(exception, errors.FileFormException): code = 400 + elif isinstance(exception, errors.FileFormException): + code = 400 elif isinstance(exception, ElasticsearchException): code = 503 message = "Search is currently down. Try again later." diff --git a/tests/integration_tests/python/test_uploads.py b/tests/integration_tests/python/test_uploads.py index e6a5729d785c90d4996c212f6bbcaa322eb36c4b..2073426fa380a9ca7b439a0ff783a1adc3bb88f6 100644 --- a/tests/integration_tests/python/test_uploads.py +++ b/tests/integration_tests/python/test_uploads.py @@ -55,6 +55,20 @@ def test_reaper_upload(data_builder, randstr, upload_file_form, as_admin): )) assert r.ok + # reaper-upload files to group_1/project_label_1 using session_uid without any files + file_form = upload_file_form( + group={'_id': group_1}, + project={'label': project_label_1, "files":[]}, + session={'uid': session_uid+"1", "files":[], 'subject': { + 'code': prefix + '-subject-code', + 'files': [] + }} + ) + print file_form + r = as_admin.post('/upload/reaper', files={"metadata": file_form.get("metadata")}) + print r.json() + assert r.status_code == 400 + # get session created by the upload project_1 = as_admin.get('/groups/' + group_1 + '/projects').json()[0]['_id'] session = as_admin.get('/projects/' + project_1 + '/sessions').json()[0]['_id'] @@ -173,6 +187,25 @@ def test_reaper_upload_unknown_group_project(data_builder, file_form, as_root, a ) assert r.ok + # Try uploading 0 files + r = as_root.post('/upload/label', files={"metadata":file_form( + 'acquisition.csv', + meta={ + 'group': {'_id': 'not_a_real_group'}, + 'project': { + 'label': 'new_project', + }, + 'session': { + 'label': 'test_session_label', + }, + 'acquisition': { + 'label': 'test_acquisition_label', + 'files': [{'name': 'acquisition.csv'}] + } + }).get("metadata")} + ) + assert r.status_code == 400 + # get session created by the upload r = as_root.get('/groups/unknown/projects') @@ -362,7 +395,7 @@ def test_uid_upload(data_builder, file_form, as_admin, as_user, as_public): # try to uid-upload w/o metadata r = as_admin.post('/upload/uid', files=file_form('test.csv')) - assert r.status_code == 500 + assert r.status_code == 400 # NOTE unused.csv is testing code that discards files not referenced from meta uid_files = ('project.csv', 'subject.csv', 'session.csv', 'acquisition.csv', 'unused.csv') @@ -390,6 +423,10 @@ def test_uid_upload(data_builder, file_form, as_admin, as_user, as_public): r = as_user.post('/upload/uid', files=file_form(*uid_files, meta=uid_meta)) assert r.status_code == 403 + # try to uid-upload no files + r = as_admin.post('/upload/uid', files={"metadata": file_form(*uid_files, meta=uid_meta).get("metadata")}) + assert r.status_code == 400 + # uid-upload files r = as_admin.post('/upload/uid', files=file_form(*uid_files, meta=uid_meta)) assert r.ok