Commit c2c6d1e8 authored by David Sikter's avatar David Sikter
Browse files

Adding new endpoint entries/edit and tests

parent a566588e
Pipeline #115335 passed with stages
in 30 minutes and 5 seconds
......@@ -48,8 +48,9 @@ from ..utils import (
create_download_stream_zipped, create_download_stream_raw_file,
DownloadItem, create_responses)
from ..models import (
Aggregation, Pagination, PaginationResponse, MetadataPagination, TermsAggregation, WithQuery, WithQueryAndPagination, MetadataRequired,
MetadataResponse, Metadata, Files, Query, User, Owner,
Aggregation, Pagination, PaginationResponse, MetadataPagination, TermsAggregation,
WithQuery, WithQueryAndPagination, MetadataRequired, MetadataResponse, Metadata,
MetadataEditRequest, MetadataEditRequestResponse, Files, Query, User, Owner,
QueryParameters, metadata_required_parameters, files_parameters, metadata_pagination_parameters,
HTTPExceptionModel)
......@@ -280,6 +281,18 @@ _bad_path_response = status.HTTP_404_NOT_FOUND, {
'model': HTTPExceptionModel,
'description': strip('File or directory not found.')}
_bad_edit_request = status.HTTP_400_BAD_REQUEST, {
'model': HTTPExceptionModel,
'description': strip('Edit request could not be executed.')}
_bad_edit_request_authorization = status.HTTP_401_UNAUTHORIZED, {
'model': HTTPExceptionModel,
'description': strip('Not enough permissions to execute edit request.')}
_bad_edit_request_empty_query = status.HTTP_404_NOT_FOUND, {
'model': HTTPExceptionModel,
'description': strip('No matching entries found.')}
_raw_download_response = 200, {
'content': {'application/zip': {}},
'description': strip('''
......@@ -1328,3 +1341,35 @@ async def post_entry_metadata_edit(
datamodel.Dataset.m_def.a_mongo.objects(dataset_id=dataset).delete()
return data
@router.post(
'/edit',
tags=[metadata_tag],
summary='Edit the user metadata of a set of entries',
response_model=MetadataEditRequestResponse,
response_model_exclude_unset=True,
response_model_exclude_none=True,
responses=create_responses(
_bad_edit_request, _bad_edit_request_authorization, _bad_edit_request_empty_query))
async def post_entries_edit(
request: Request,
data: MetadataEditRequest,
user: User = Depends(create_user_dependency(required=True))):
'''
Updates the metadata of the specified entries.
**Note:**
- Only admins can edit some of the fields.
- Only entry level attributes (like `comment`, `references` etc.) can be set using
this endpoint; upload level attributes (like `upload_name`, `coauthors`, embargo
settings, etc) need to be set through the endpoint **uploads/upload_id/edit**.
- If the upload is published, the only operation permitted using this endpoint is to
edit the members of datasets you own.
'''
edit_request_json = await request.json()
response, status_code = proc.MetadataEditRequestHandler.edit_metadata(
edit_request_json=edit_request_json, upload_id=None, user=user)
if status_code != status.HTTP_200_OK and not data.verify_only:
raise HTTPException(status_code=status_code, detail=response.error)
return response
......@@ -975,13 +975,13 @@ async def post_upload_edit(
- Only admins can edit some of the fields.
- The embargo of a published upload is lifted by setting the `embargo_length` attribute
to 0.
- If the upload is published, the only operation permitted for non-admin users is to
lift the embargo, i.e. set `embargo_length` to 0, and add/remove entries from the
user's datasets.
- If the upload is published, the only operations permitted using this endpoint is to
lift the embargo, i.e. set `embargo_length` to 0, and to edit the members of datasets
you own.
- If a query is specified, it is not possible to edit upload level metadata (like
`upload_name`, `coauthors`, etc.), as the purpose of queries is to select only a
subset of the upload entries to edit, but changing upload level metadata would affect
all entries of the upload.
**all** entries of the upload.
'''
edit_request_json = await request.json()
response, status_code = MetadataEditRequestHandler.edit_metadata(
......
......@@ -17,6 +17,7 @@
#
import pytest
from datetime import datetime
from nomad import utils
from nomad.search import search
......@@ -24,6 +25,9 @@ from nomad.datamodel import Dataset
from nomad import processing as proc
from tests.utils import ExampleData
from tests.app.v1.routers.common import assert_response
from tests.processing.test_edit_metadata import (
assert_metadata_edited, all_coauthor_entry_metadata, all_admin_entry_metadata)
logger = utils.get_logger(__name__)
......@@ -301,3 +305,122 @@ class TestEditRepo():
def test_admin_only(self, other_test_user):
rv = self.perform_edit(main_author=other_test_user.user_id)
assert rv.status_code != 200
@pytest.mark.parametrize('user, kwargs', [
pytest.param(
'test_user', dict(
query={'upload_id': 'id_unpublished_w'},
metadata=all_coauthor_entry_metadata,
affected_upload_ids=['id_unpublished_w']),
id='edit-all'),
pytest.param(
'admin_user', dict(
query={'upload_id': 'id_published_w'},
owner='all',
metadata=all_admin_entry_metadata,
affected_upload_ids=['id_published_w']),
id='protected-admin'),
pytest.param(
'test_user', dict(
query={'upload_id': 'id_unpublished_w'},
metadata=all_admin_entry_metadata,
expected_status_code=401),
id='protected-not-admin'),
pytest.param(
'admin_user', dict(
query={'upload_id': 'id_published_w'},
owner='all',
metadata=dict(comment='test comment'),
affected_upload_ids=['id_published_w']),
id='published-admin'),
pytest.param(
'test_user', dict(
query={'upload_id': 'id_published_w'},
metadata=dict(comment='test comment'),
expected_status_code=401),
id='published-not-admin'),
pytest.param(
None, dict(
owner='all',
query={'upload_id': 'id_unpublished_w'},
metadata=dict(comment='test comment'),
expected_status_code=401),
id='no-credentials'),
pytest.param(
'invalid', dict(
owner='all',
query={'upload_id': 'id_unpublished_w'},
metadata=dict(comment='test comment'),
expected_status_code=401),
id='invalid-credentials'),
pytest.param(
'other_test_user', dict(
query={'upload_id': 'id_unpublished_w'},
metadata=dict(comment='test comment'),
expected_status_code=404),
id='no-access'),
pytest.param(
'other_test_user', dict(
query={'upload_id': 'id_unpublished_w'},
metadata=dict(comment='test comment'),
affected_upload_ids=['id_unpublished_w'],
add_coauthor=True),
id='coauthor-access'),
pytest.param(
'test_user', dict(
query={'and': [{'upload_create_time:gt': '2021-01-01'}, {'published': False}]},
metadata=dict(comment='a test comment'),
affected_upload_ids=['id_unpublished_w']),
id='compound-query-ok'),
pytest.param(
'test_user', dict(
query={'upload_id': 'id_unpublished_w'},
metadata=dict(upload_name='a test name'),
expected_status_code=400),
id='query-cannot-edit-upload-data'),
pytest.param(
'test_user', dict(
query={'upload_create_time:lt': '2021-01-01'},
metadata=dict(comment='a test comment'),
expected_status_code=404),
id='query-no-results')])
def test_post_entries_edit(
client, proc_infra, example_data_writeable, a_dataset, test_auth_dict, test_users_dict,
user, kwargs):
'''
Note, since the endpoint basically just forwards the request to
`MetadataEditRequestHandler.edit_metadata`, we only do very simple verification here,
the more extensive testnig is done in `tests.processing.test_edit_metadata`.
'''
user_auth, _token = test_auth_dict[user]
user = test_users_dict.get(user)
query = kwargs.get('query')
owner = kwargs.get('owner', 'visible')
metadata = kwargs.get('metadata')
entries = kwargs.get('entries')
entries_key = kwargs.get('entries_key')
verify_only = kwargs.get('verify_only', False)
expected_status_code = kwargs.get('expected_status_code', 200)
if expected_status_code == 200 and not verify_only:
affected_upload_ids = kwargs.get('affected_upload_ids')
expected_metadata = kwargs.get('expected_metadata', metadata)
add_coauthor = kwargs.get('add_coauthor', False)
if add_coauthor:
upload = proc.Upload.get(affected_upload_ids[0])
upload.edit_upload_metadata(
edit_request_json={'metadata': {'coauthors': user.user_id}}, user_id=upload.main_author)
upload.block_until_complete()
edit_request_json = dict(
query=query, owner=owner, metadata=metadata, entries=entries, entries_key=entries_key,
verify_only=verify_only)
url = 'entries/edit'
edit_start = datetime.utcnow().isoformat()[0:22]
response = client.post(url, headers=user_auth, json=edit_request_json)
assert_response(response, expected_status_code)
if expected_status_code == 200:
assert_metadata_edited(
user, None, query, metadata, entries, entries_key, verify_only,
expected_status_code, expected_metadata, affected_upload_ids, edit_start)
......@@ -50,6 +50,9 @@ all_admin_metadata = dict(
license='a license',
main_author='lhofstadter')
all_admin_entry_metadata = {
k: v for k, v in all_admin_metadata.items() if k not in _mongo_upload_metadata}
def assert_edit_request(user, **kwargs):
# Extract test parameters (lots of defaults)
......@@ -136,6 +139,8 @@ def convert_to_comparable_value(quantity, value, from_format, user):
'''
if quantity.is_scalar:
return convert_to_comparable_value_single(quantity, value, from_format, user)
if value is None and from_format == 'es':
return []
if type(value) != list:
value = [value]
return [convert_to_comparable_value_single(quantity, v, from_format, user) for v in value]
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment