diff --git a/nomad/api.py b/nomad/api.py index 438239c943c2efce122befc5f0c90a8e44f63053..e75833e8ac7d3253e723b892c9f63c90a7dddd65 100644 --- a/nomad/api.py +++ b/nomad/api.py @@ -63,6 +63,7 @@ def login_really_required(func): abort(401, message='Anonymous access is forbidden, authorization required') else: return func(*args, **kwargs) + wrapper.__name__ = func.__name__ return wrapper @@ -300,6 +301,53 @@ class UploadRes(Resource): return result, 200 + @login_really_required + def post(self, upload_id): + """ + Move an upload out of the staging area. This changes the visibility of the upload. + Clients can specify, if the calcs should be restricted. + + .. :quickref: upload; Move an upload out of the staging area. + + **Example request**: + + .. sourcecode:: http + + POST /nomadxt/api/uploads HTTP/1.1 + Accept: application/json + Content-Type: application/json + + { + operation: 'unstage' + } + + + :param string upload_id: the upload id + :resheader Content-Type: application/json + :status 200: upload unstaged successfully + :status 404: upload could not be found + :status 400: if the operation is not supported + :returns: the upload record + """ + try: + upload = Upload.get(upload_id) + except KeyError: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + if upload.user_id != g.user.email: + abort(404, message='Upload with id %s does not exist.' % upload_id) + + json_data = request.get_json() + if json_data is None: + json_data = {} + + operation = json_data.get('operation') + if operation == 'unstage': + upload.unstage() + return upload.json_dict, 200 + + abort(400, message='Unsuported operation %s.' % operation) + @login_really_required def delete(self, upload_id): """ diff --git a/nomad/processing/data.py b/nomad/processing/data.py index fd7f0de64f38d57f4ecb0e792e34862645e0b3b2..ae921ffbaac40ee1005532f8ed969702e0f2ad02 100644 --- a/nomad/processing/data.py +++ b/nomad/processing/data.py @@ -234,7 +234,7 @@ class Upload(Proc): @classmethod def user_uploads(cls, user: User) -> List['Upload']: """ Returns all uploads for the given user. Currently returns all uploads. """ - return cls.objects() + return cls.objects(user_id=user.email, in_staging=True) def get_logger(self, **kwargs): logger = super().get_logger() @@ -263,7 +263,7 @@ class Upload(Proc): files.delete_archives(upload_hash=self.upload_hash) # delete repo entries - RepoCalc.search().query('match', upload_id=self.upload_id).delete() + RepoCalc.delete_upload(upload_id=self.upload_id) # delete calc processings Calc.objects(upload_id=self.upload_id).delete() @@ -304,6 +304,11 @@ class Upload(Proc): else: return False + def unstage(self): + self.in_staging = False + RepoCalc.update_upload(upload_id=self.upload_id, staging=False) + self.save() + @property def json_dict(self) -> dict: """ A json serializable dictionary representation. """ diff --git a/nomad/repo.py b/nomad/repo.py index 32cc04a0f6c7bba0c05f6561d22f367a7cb671c6..526255caabf149fd18cb892c960144e8f80da848 100644 --- a/nomad/repo.py +++ b/nomad/repo.py @@ -148,6 +148,17 @@ class RepoCalc(ElasticDocument): return calc + @staticmethod + def delete_upload(upload_id): + """ Deletes all repo entries of the given upload. """ + RepoCalc.search().query('match', upload_id=upload_id).delete() + + @classmethod + def update_upload(cls, upload_id, **kwargs): + """ Update all entries of given upload with keyword args. """ + for calc in RepoCalc.search().query('match', upload_id=upload_id): + calc.update(**kwargs) + @staticmethod def es_search(body): """ Perform an elasticsearch and not elasticsearch_dsl search on the Calc index. """ diff --git a/tests/test_api.py b/tests/test_api.py index a570edf7c36fce831c2e64098b5981dac0f448f9..19b8030c608e0c776d1aab4425ae53f73be68ddd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -216,7 +216,16 @@ def test_processing(client, file, worker, mocksearch, test_user_auth): upload = assert_upload(rv.data) assert len(upload['calcs']['results']) == 1 - time.sleep(1) + rv = client.post( + '/uploads/%s' % upload['upload_id'], + headers=test_user_auth, + data=json.dumps(dict(operation='unstage')), + content_type='application/json') + assert rv.status_code == 200 + + rv = client.get('/uploads', headers=test_user_auth) + assert rv.status_code == 200 + assert_uploads(rv.data, count=0) def test_repo_calc(client, example_elastic_calc): diff --git a/tests/test_repo.py b/tests/test_repo.py index fd3347ee4a9f9c6f7dfb9bfb6e640ad76a61236c..a9058bd1a2ca07e6fe076eed263973028d05b25a 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -110,3 +110,13 @@ def test_delete_elastic_calc(example_elastic_calc: RepoCalc, caplog): assert False finally: caplog.set_level(logging.WARNING) + + +def test_staging_elastic_calc(example_elastic_calc: RepoCalc): + assert RepoCalc.get(id='test_upload_hash/test_calc_hash').staging + + +def test_unstage_elastic_calc(example_elastic_calc: RepoCalc): + RepoCalc.update_upload(upload_id='test_upload_id', staging=False) + + assert not RepoCalc.get(id='test_upload_hash/test_calc_hash').staging