From b70ff50d0debd375f5f8ff9cf06218fc8629dc2a Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Fri, 29 Dec 2023 18:35:50 -0500 Subject: [PATCH] new: `Database.execute()` --- arango/database.py | 31 +++++++++++++++++++++++++++++++ arango/exceptions.py | 4 ++++ tests/static/cluster-3.10.conf | 1 + tests/static/cluster.conf | 1 + tests/static/single-3.10.conf | 1 + tests/static/single.conf | 1 + tests/test_database.py | 6 ++++++ 7 files changed, 45 insertions(+) diff --git a/arango/database.py b/arango/database.py index e33bd5ae..d0fc59b9 100644 --- a/arango/database.py +++ b/arango/database.py @@ -44,6 +44,7 @@ ServerEchoError, ServerEncryptionError, ServerEngineError, + ServerExecuteError, ServerLicenseGetError, ServerLicenseSetError, ServerLogLevelError, @@ -224,6 +225,36 @@ def response_handler(resp: Response) -> Json: return self._execute(request, response_handler) + def execute(self, command: str) -> Result[Any]: + """Execute raw Javascript command on the server. + + Executes the JavaScript code in the body on the server as + the body of a function with no arguments. If you have a + return statement then the return value you produce will be returned + as 'application/json'. + + NOTE: this method endpoint will only be usable if the server + was started with the option `--javascript.allow-admin-execute true`. + The default value of this option is false, which disables the execution + of user-defined code and disables this API endpoint entirely. + This is also the recommended setting for production. + + :param command: Javascript command to execute. + :type command: str + :return: Return value of **command**, if any. + :rtype: Any + :raise arango.exceptions.ServerExecuteError: If execution fails. + """ + request = Request(method="post", endpoint="/_admin/execute", data=command) + + def response_handler(resp: Response) -> Any: + if not resp.is_success: + raise ServerExecuteError(resp, request) + + return resp.body + + return self._execute(request, response_handler) + def execute_transaction( self, command: str, diff --git a/arango/exceptions.py b/arango/exceptions.py index fb11f8d5..cd697623 100644 --- a/arango/exceptions.py +++ b/arango/exceptions.py @@ -698,6 +698,10 @@ class ServerEncryptionError(ArangoServerError): """Failed to reload user-defined encryption keys.""" +class ServerExecuteError(ArangoServerError): + """Failed to execute raw JavaScript command.""" + + ##################### # Task Exceptions # ##################### diff --git a/tests/static/cluster-3.10.conf b/tests/static/cluster-3.10.conf index 573c030a..3cc40152 100644 --- a/tests/static/cluster-3.10.conf +++ b/tests/static/cluster-3.10.conf @@ -10,3 +10,4 @@ jwt-secret = /tests/static/keyfile [args] all.database.password = passwd all.log.api-enabled = true +all.javascript.allow-admin-execute = true \ No newline at end of file diff --git a/tests/static/cluster.conf b/tests/static/cluster.conf index 182f3d17..b98dc3ec 100644 --- a/tests/static/cluster.conf +++ b/tests/static/cluster.conf @@ -11,3 +11,4 @@ jwt-secret = /tests/static/keyfile all.database.password = passwd all.database.extended-names = true all.log.api-enabled = true +all.javascript.allow-admin-execute = true \ No newline at end of file diff --git a/tests/static/single-3.10.conf b/tests/static/single-3.10.conf index c982303b..3d82808d 100644 --- a/tests/static/single-3.10.conf +++ b/tests/static/single-3.10.conf @@ -8,3 +8,4 @@ jwt-secret = /tests/static/keyfile [args] all.database.password = passwd +all.javascript.allow-admin-execute = true \ No newline at end of file diff --git a/tests/static/single.conf b/tests/static/single.conf index e880f9d5..b176ad25 100644 --- a/tests/static/single.conf +++ b/tests/static/single.conf @@ -9,3 +9,4 @@ jwt-secret = /tests/static/keyfile [args] all.database.password = passwd all.database.extended-names = true +all.javascript.allow-admin-execute = true \ No newline at end of file diff --git a/tests/test_database.py b/tests/test_database.py index da6307a4..0a0c7d29 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -263,6 +263,12 @@ def test_database_misc_methods(sys_db, db, bad_db, cluster): bad_db.engine() assert err.value.error_code in {11, 1228} + # Test execute JavaScript code + assert db.execute(1) == None + assert db.execute(None) == {"error": False, "code": 200} + assert db.execute("") == {"error": False, "code": 200} + assert db.execute("return 1") == 1 + def test_database_management(db, sys_db, bad_db): # Test list databases