From b70ff50d0debd375f5f8ff9cf06218fc8629dc2a Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Fri, 29 Dec 2023 18:35:50 -0500 Subject: [PATCH 1/4] 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 From d62a0ff175ed04a3f24c590bf074f87589af713e Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Fri, 29 Dec 2023 18:36:56 -0500 Subject: [PATCH 2/4] fix lint --- tests/test_database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_database.py b/tests/test_database.py index 0a0c7d29..4dafb5e4 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -264,7 +264,7 @@ def test_database_misc_methods(sys_db, db, bad_db, cluster): assert err.value.error_code in {11, 1228} # Test execute JavaScript code - assert db.execute(1) == None + assert db.execute(1) is None assert db.execute(None) == {"error": False, "code": 200} assert db.execute("") == {"error": False, "code": 200} assert db.execute("return 1") == 1 From 83faff40e12823b21896e1ff8f003ce60b84f2ec Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Fri, 29 Dec 2023 18:41:35 -0500 Subject: [PATCH 3/4] fix end of file --- tests/static/cluster-3.10.conf | 2 +- tests/static/cluster.conf | 2 +- tests/static/single-3.10.conf | 2 +- tests/static/single.conf | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/static/cluster-3.10.conf b/tests/static/cluster-3.10.conf index 3cc40152..d7732c90 100644 --- a/tests/static/cluster-3.10.conf +++ b/tests/static/cluster-3.10.conf @@ -10,4 +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 +all.javascript.allow-admin-execute = true diff --git a/tests/static/cluster.conf b/tests/static/cluster.conf index b98dc3ec..86f78556 100644 --- a/tests/static/cluster.conf +++ b/tests/static/cluster.conf @@ -11,4 +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 +all.javascript.allow-admin-execute = true diff --git a/tests/static/single-3.10.conf b/tests/static/single-3.10.conf index 3d82808d..09d1d9f3 100644 --- a/tests/static/single-3.10.conf +++ b/tests/static/single-3.10.conf @@ -8,4 +8,4 @@ jwt-secret = /tests/static/keyfile [args] all.database.password = passwd -all.javascript.allow-admin-execute = true \ No newline at end of file +all.javascript.allow-admin-execute = true diff --git a/tests/static/single.conf b/tests/static/single.conf index b176ad25..df45cb76 100644 --- a/tests/static/single.conf +++ b/tests/static/single.conf @@ -9,4 +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 +all.javascript.allow-admin-execute = true From dbea79e95503d8e8c2f14ca671e00da1cd02294d Mon Sep 17 00:00:00 2001 From: Anthony Mahanna Date: Fri, 26 Jan 2024 12:44:55 -0500 Subject: [PATCH 4/4] fix: lint --- arango/replication.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/arango/replication.py b/arango/replication.py index 0ecfc20e..d5fae457 100644 --- a/arango/replication.py +++ b/arango/replication.py @@ -180,13 +180,15 @@ def response_handler(resp: Response) -> Json: if resp.is_success: result = format_replication_header(resp.headers) result["content"] = [ - [ - self._conn.deserialize(line) - for line in resp.body.split("\n") - if line - ] - if deserialize - else resp.body + ( + [ + self._conn.deserialize(line) + for line in resp.body.split("\n") + if line + ] + if deserialize + else resp.body + ) ] return result