diff --git a/nomad/api/admin.py b/nomad/api/admin.py index ea6a8d006b9639fd54683145fdcde4d3e127532c..02a88afa55b7c7c2d52a9e0e7ad2b8c0690c9dea 100644 --- a/nomad/api/admin.py +++ b/nomad/api/admin.py @@ -24,40 +24,49 @@ from .auth import login_really_required ns = api.namespace('admin', description='Administrative operations') -@ns.route('/<string:operation>') -@api.doc(params={'operation': 'The operation to perform.'}) -class AdminOperationsResource(Resource): - @api.doc('exec_admin_command') - @api.response(200, 'Operation performed') - @api.response(404, 'Operation does not exist') - @api.response(400, 'Operation not available/disabled') +@ns.route('/reset') +class AdminRemoveResource(Resource): + @api.doc('exec_reset_command') + @api.response(200, 'Reset performed') + @api.response(400, 'Reset not available/disabled') @login_really_required - def post(self, operation): + def post(self): """ - Allows to perform administrative operations on the nomad services. - - The possible operations are ``reset`` and ``remove``. - The ``reset`` operation will attempt to clear the contents of all databased and indices. + Nomad can be configured to disable reset and the operation might not be available. + """ + if not g.user.is_admin: + abort(401, message='Only the admin user can perform reset.') + + if config.services.disable_reset: + abort(400, message='Operation is disabled') + + infrastructure.reset() + + return dict(messager='Reset performed.'), 200 + + +@ns.route('/remove') +class AdminResetResource(Resource): + @api.doc('exec_remove_command') + @api.response(200, 'Remove performed') + @api.response(400, 'Remove not available/disabled') + @login_really_required + def post(self): + """ The ``remove``operation will attempt to remove all databases. Expect the api to stop functioning after this request. - Reset and remove can be disabled. + Nomad can be configured to disable remove and the operation might not be available. """ if not g.user.is_admin: - abort(401, message='Only the admin user can perform this operation.') - - if operation == 'reset': - if config.services.disable_reset: - abort(400, message='Operation is disabled') - infrastructure.reset() - elif operation == 'remove': - if config.services.disable_reset: - abort(400, message='Operation is disabled') - infrastructure.remove() - else: - abort(404, message='Unknown operation %s' % operation) - - return dict(messager='Operation %s performed.' % operation), 200 + abort(401, message='Only the admin user can perform remove.') + + if config.services.disable_reset: + abort(400, message='Operation is disabled') + + infrastructure.remove() + + return dict(messager='Remove performed.'), 200 diff --git a/nomad/api/app.py b/nomad/api/app.py index 43cc6866870e89f8769c2d11de79df1c25549ea5..30774675fa600d2664c50f1b74bc5fde1027e3eb 100644 --- a/nomad/api/app.py +++ b/nomad/api/app.py @@ -61,7 +61,7 @@ api = Api( @app.errorhandler(Exception) -@api.errorhandler(Exception) +@api.errorhandler def handle(error: Exception): status_code = getattr(error, 'code', 500) name = getattr(error, 'name', 'Internal Server Error') diff --git a/nomad/client.py b/nomad/client.py index dd1c64130e7713b542ff8e5d2866a7b7a74a8a1a..ec49277904d17ad7c9c0292583af4b16d86a56eb 100644 --- a/nomad/client.py +++ b/nomad/client.py @@ -49,11 +49,9 @@ def create_client( user: str = user, password: str = pw): """ A factory method to create the client. """ + http_client = RequestsClient() if user is not None: - http_client = RequestsClient() http_client.set_basic_auth(host, user, pw) - else: - http_client = None client = SwaggerClient.from_url( 'http://%s:%d%s/swagger.json' % (host, port, base_path), @@ -299,7 +297,7 @@ def upload(path, name: str, offline: bool, unstage: bool): @cli.command(help='Attempts to reset the nomad.') def reset(): - _cli_client().admin.exec_admin_command(operation='reset').reponse() + _cli_client().admin.exec_reset_command().response() @cli.command(help='Run processing locally.') diff --git a/nomad/parsing/backend.py b/nomad/parsing/backend.py index f16a2cafcfc0b560d5afc7d852eb3d75d709d64e..76176d8076cfbda494eead798b75a463e2b73801 100644 --- a/nomad/parsing/backend.py +++ b/nomad/parsing/backend.py @@ -458,17 +458,19 @@ class LocalBackend(LegacyParserBackend): sections = self._delegate.results[meta_name] return [section.gIndex for section in sections] - @staticmethod def _write( - json_writer: JSONStreamWriter, - value: Any, + self, json_writer: JSONStreamWriter, value: Any, filter: Callable[[str, Any], Any] = None): if isinstance(value, list): - json_writer.open_array() - for item in value: - LocalBackend._write(json_writer, item, filter=filter) - json_writer.close_array() + if len(value) == 1 and isinstance(value[0], Section) and \ + not self._delegate.metaInfoEnv().infoKindEl(value[0].name).repeats: + self._write(json_writer, value[0], filter=filter) + else: + json_writer.open_array() + for item in value: + self._write(json_writer, item, filter=filter) + json_writer.close_array() elif isinstance(value, Section): section = value @@ -482,7 +484,7 @@ class LocalBackend(LegacyParserBackend): if value is not None: json_writer.key(name) - LocalBackend._write(json_writer, value, filter=filter) + self._write(json_writer, value, filter=filter) json_writer.close_object() @@ -491,10 +493,11 @@ class LocalBackend(LegacyParserBackend): def _obj(self, value: Any, filter: Callable[[str, Any], Any] = None) -> Any: if isinstance(value, list): - if len(value) == 1 and isinstance(value[0], Section) and not self._delegate.metaInfoEnv().infoKindEl(value[0].name).repeats: - return self._obj(value[0]) + if len(value) == 1 and isinstance(value[0], Section) and \ + not self._delegate.metaInfoEnv().infoKindEl(value[0].name).repeats: + return self._obj(value[0], filter=filter) else: - return [self._obj(item) for item in value] + return [self._obj(item, filter=filter) for item in value] elif isinstance(value, Section): section = value @@ -543,10 +546,7 @@ class LocalBackend(LegacyParserBackend): # TODO the root sections should be determined programatically for root_section in ['section_run', 'section_calculation_info', 'section_repository_info']: json_writer.key(root_section) - json_writer.open_array() - for section in self._delegate.results[root_section]: - LocalBackend._write(json_writer, section, filter=filter) - json_writer.close_array() + self._write(json_writer, self._delegate.results[root_section], filter=filter) json_writer.close_object() json_writer.close() diff --git a/tests/test_api.py b/tests/test_api.py index d548b096c73410acef1ecbc6e254fee0943bf5bd..57fd830b46fa7be91e543055f82c48b02b2327e0 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -98,7 +98,7 @@ class TestAdmin: assert rv.status_code == 404 def test_only_admin(self, client, test_user_auth): - rv = client.post('/admin/doesnotexist', headers=test_user_auth) + rv = client.post('/admin/reset', headers=test_user_auth) assert rv.status_code == 401 @pytest.fixture(scope='function')