From b198fdf2179700306f113e5142e5f95b335a771d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 5 Oct 2023 12:17:26 +0200 Subject: [PATCH 01/62] CI: updated jackson version --- .github/workflows/test.yml | 2 +- .gitignore | 1 + pom.xml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f7887694..92b69628e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -165,7 +165,7 @@ jobs: fail-fast: false matrix: jackson-version: - - 2.15.1 + - 2.15.2 - 2.14.3 - 2.13.5 - 2.12.7 diff --git a/.gitignore b/.gitignore index 11ee1822e..ae4e4673f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ test-results-native /resilience-tests/bin/toxiproxy-server-linux-amd64 dependency-reduced-pom.xml +/bin/ diff --git a/pom.xml b/pom.xml index 816c8a123..d4f58438c 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ true arangodb-1 https://sonarcloud.io - 2.15.1 + 2.15.2 From d908d264243c9054999a6ab5b64b3ee4a31d01cd Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 5 Oct 2023 15:26:42 +0200 Subject: [PATCH 02/62] updated native image test libraries --- driver/pom.xml | 4 ++-- pom.xml | 4 ++-- shaded/pom.xml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/driver/pom.xml b/driver/pom.xml index fc4fc6dbd..88e89495b 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -186,8 +186,8 @@ org.graalvm.sdk graal-sdk - 22.3.2 - test + 23.0.1 + provided io.smallrye.config diff --git a/pom.xml b/pom.xml index d4f58438c..90572ae7f 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ org.junit junit-bom - 5.9.3 + 5.10.0 pom import @@ -398,7 +398,7 @@ org.graalvm.buildtools native-maven-plugin - 0.9.21 + 0.9.27 true diff --git a/shaded/pom.xml b/shaded/pom.xml index 71a2b126b..1773a43b8 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -49,7 +49,7 @@ org.graalvm.sdk graal-sdk - 22.3.2 + 23.0.1 provided From c0278f481c12c58eb90dd5ea069f4f4f7ca95140 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 5 Oct 2023 15:55:16 +0200 Subject: [PATCH 03/62] fixed native tests --- .../META-INF/native-image/reflect-config.json | 97 ++++++++++++------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/driver/src/test/resources/META-INF/native-image/reflect-config.json b/driver/src/test/resources/META-INF/native-image/reflect-config.json index 50aea7d0b..db03957e0 100644 --- a/driver/src/test/resources/META-INF/native-image/reflect-config.json +++ b/driver/src/test/resources/META-INF/native-image/reflect-config.json @@ -1,7 +1,12 @@ [ { - "name":"org.junit.jupiter.engine.extension.TimeoutInvocationFactory$SingleThreadExecutorResource", - "methods":[{"name":"","parameterTypes":[] }] + "name": "org.junit.jupiter.engine.extension.TimeoutInvocationFactory$SingleThreadExecutorResource", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] }, { "name": "org.junit.jupiter.engine.extension.TimeoutInvocationFactory$SingleThreadExecutorResource", @@ -14,19 +19,19 @@ }, { "name": "com.arangodb.ArangoCollectionTest$TestUpdateEntity", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allPublicMethods":true, - "allDeclaredConstructors":true, - "allDeclaredClasses":true + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true }, { "name": "com.arangodb.ArangoCollectionTest$TestUpdateEntitySerializeNullFalse", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allPublicMethods":true, - "allDeclaredConstructors":true, - "allDeclaredClasses":true + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true }, { "name": "com.arangodb.ArangoDatabaseTest$TransactionTestEntity", @@ -168,33 +173,57 @@ ] }, { - "name":"com.arangodb.ArangoCollectionTest$Animal", - "allDeclaredMethods":true, - "allPublicMethods":true, - "allDeclaredClasses":true + "name": "com.arangodb.ArangoCollectionTest$Animal", + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionTest$AnnotatedEntity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionTest$Cat", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionTest$Dog", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true }, { - "name":"com.arangodb.ArangoCollectionTest$AnnotatedEntity", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allPublicMethods":true, - "allDeclaredConstructors":true, - "allDeclaredClasses":true + "name": "com.arangodb.serde.JacksonInterferenceTest", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true }, { - "name":"com.arangodb.ArangoCollectionTest$Cat", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allPublicMethods":true, - "allDeclaredConstructors":true, - "allDeclaredClasses":true + "name": "com.arangodb.serde.JacksonInterferenceTest$FooField", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true }, { - "name":"com.arangodb.ArangoCollectionTest$Dog", - "allDeclaredFields":true, - "allDeclaredMethods":true, - "allPublicMethods":true, - "allDeclaredConstructors":true, - "allDeclaredClasses":true + "name": "com.arangodb.serde.JacksonInterferenceTest$FooProp", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true } ] From ae41496e83005d2157fd26d4aa485b65aae3d961 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 5 Oct 2023 17:41:23 +0200 Subject: [PATCH 04/62] fixed native integration tests --- integration-tests/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 71f687895..89ef8b884 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -184,7 +184,7 @@ ${project.build.directory}/generated-test-sources replacer - **/CustomSerdeTest.**,**/SerdeTest.**,**/SerializableTest.** + **/CustomSerdeTest.**,**/SerdeTest.**,**/SerializableTest.**,**/JacksonInterferenceTest.** From 659e94898def8787149398d830d87272b6a66e47 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 6 Oct 2023 11:14:32 +0200 Subject: [PATCH 05/62] deps upd --- driver/pom.xml | 2 +- http/pom.xml | 12 ------------ pom.xml | 13 ++++++++++--- shaded/pom.xml | 2 +- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/driver/pom.xml b/driver/pom.xml index 88e89495b..5a4156c4d 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -186,7 +186,7 @@ org.graalvm.sdk graal-sdk - 23.0.1 + 22.3.3 provided diff --git a/http/pom.xml b/http/pom.xml index 631cd039f..da9b41ad7 100644 --- a/http/pom.xml +++ b/http/pom.xml @@ -30,18 +30,6 @@ - - - - io.vertx - vertx-stack-depchain - 4.4.2 - pom - import - - - - diff --git a/pom.xml b/pom.xml index 90572ae7f..3c35aa73f 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ org.slf4j slf4j-api - 2.0.7 + 2.0.9 com.google.code.findbugs @@ -172,7 +172,7 @@ org.slf4j slf4j-simple - 2.0.7 + 2.0.9 org.assertj @@ -182,13 +182,20 @@ com.tngtech.archunit archunit-junit5 - 1.0.1 + 1.1.0 org.eclipse yasson 3.0.3 + + io.vertx + vertx-stack-depchain + 4.4.2 + pom + import + diff --git a/shaded/pom.xml b/shaded/pom.xml index 1773a43b8..854ce4afd 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -49,7 +49,7 @@ org.graalvm.sdk graal-sdk - 23.0.1 + 22.3.3 provided From 244aca1b9345693dd8bce3edede7515431c2d8fb Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 10 Oct 2023 09:46:44 +0200 Subject: [PATCH 06/62] HttpConnection async refactoring --- .github/workflows/resilience.yml | 2 +- .../com/arangodb/http/HttpCommunication.java | 53 ++++++++++++------- .../com/arangodb/http/HttpConnection.java | 30 +++++------ 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/.github/workflows/resilience.yml b/.github/workflows/resilience.yml index cfba2570c..e5f5a8302 100644 --- a/.github/workflows/resilience.yml +++ b/.github/workflows/resilience.yml @@ -21,7 +21,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v2 with: - java-version: 19 + java-version: 21 distribution: 'adopt' cache: maven - name: Start Database diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index fa00b150a..2a522d71f 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -37,6 +37,7 @@ import java.io.Closeable; import java.io.IOException; import java.net.SocketTimeoutException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; @@ -78,7 +79,16 @@ private InternalResponse execute(final InternalRequest request, final HostHandle String body = request.getBody() == null ? "" : serde.toJsonString(request.getBody()); LOGGER.debug("Send Request [id={}]: {} {}", reqId, request, body); } - final InternalResponse response = connection.execute(request); + final InternalResponse response; + try { + response = connection.executeAsync(request).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw ArangoDBException.wrap(e); + } catch (ExecutionException e) { + throw ArangoDBException.wrap(e.getCause()); + } + if (LOGGER.isDebugEnabled()) { String body = response.getBody() == null ? "" : serde.toJsonString(response.getBody()); LOGGER.debug("Received Response [id={}]: {} {}", reqId, response, body); @@ -87,25 +97,30 @@ private InternalResponse execute(final InternalRequest request, final HostHandle hostHandler.success(); hostHandler.confirm(); return response; - } catch (final SocketTimeoutException e) { - // SocketTimeoutException exceptions are wrapped and rethrown. - TimeoutException te = new TimeoutException(e.getMessage()); - te.initCause(e); - throw new ArangoDBException(te, reqId); - } catch (final IOException e) { - hostHandler.fail(e); - if (hostHandle != null && hostHandle.getHost() != null) { - hostHandle.setHost(null); - } - final Host failedHost = host; - host = hostHandler.get(hostHandle, accessType); - if (host != null && isSafe(request)) { - LOGGER.warn("Could not connect to {} while executing request [id={}]", - failedHost.getDescription(), reqId, e); - LOGGER.debug("Try connecting to {}", host.getDescription()); + } catch (final ArangoDBException e) { + Throwable cause = e.getCause(); + if (cause instanceof SocketTimeoutException) { + // SocketTimeoutException exceptions are wrapped and rethrown. + TimeoutException te = new TimeoutException(cause.getMessage()); + te.initCause(cause); + throw new ArangoDBException(te, reqId); + } else if (cause instanceof IOException) { + hostHandler.fail((IOException) cause); + if (hostHandle != null && hostHandle.getHost() != null) { + hostHandle.setHost(null); + } + final Host failedHost = host; + host = hostHandler.get(hostHandle, accessType); + if (host != null && isSafe(request)) { + LOGGER.warn("Could not connect to {} while executing request [id={}]", + failedHost.getDescription(), reqId, cause); + LOGGER.debug("Try connecting to {}", host.getDescription()); + } else { + LOGGER.error(cause.getMessage(), cause); + throw new ArangoDBException(cause, reqId); + } } else { - LOGGER.error(e.getMessage(), e); - throw new ArangoDBException(e, reqId); + throw e; } } } diff --git a/http/src/main/java/com/arangodb/http/HttpConnection.java b/http/src/main/java/com/arangodb/http/HttpConnection.java index 69e2a1023..cea75ab38 100644 --- a/http/src/main/java/com/arangodb/http/HttpConnection.java +++ b/http/src/main/java/com/arangodb/http/HttpConnection.java @@ -57,7 +57,6 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; @@ -221,26 +220,21 @@ private HttpMethod requestTypeToHttpMethod(RequestType requestType) { } } - public InternalResponse execute(final InternalRequest request) throws IOException { + public CompletableFuture executeAsync(final InternalRequest request) { CompletableFuture rfuture = new CompletableFuture<>(); vertx.runOnContext(e -> doExecute(request, rfuture)); - InternalResponse resp; - try { - resp = rfuture.get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw ArangoDBException.wrap(e); - } catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof TimeoutException) { - throw ArangoDBException.wrap(cause); - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else { - throw new IOException(cause); + return rfuture.handle((r, e) -> { + if (e instanceof TimeoutException) { + throw new ArangoDBException(e); } - } - return resp; + if (e instanceof IOException) { + throw new ArangoDBException(e); + } + if (e != null) { + throw new ArangoDBException(new IOException(e)); + } + return r; + }); } public void doExecute(final InternalRequest request, final CompletableFuture rfuture) { From 9fe302d992f2926ea489f0c4b6531f88f4b3cb6e Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 10 Oct 2023 12:30:49 +0200 Subject: [PATCH 07/62] HttpConnection async refactoring --- .../arangodb/internal/util/ResponseUtils.java | 21 +++ .../com/arangodb/http/HttpCommunication.java | 131 +++++++++++------- .../com/arangodb/http/HttpConnection.java | 26 ++-- 3 files changed, 108 insertions(+), 70 deletions(-) diff --git a/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java b/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java index 73030fc8c..cd9049e3c 100644 --- a/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java +++ b/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java @@ -42,6 +42,7 @@ private ResponseUtils() { super(); } + // FIXME: remove public static void checkError(final InternalSerde util, final InternalResponse response) { final int responseCode = response.getResponseCode(); if (responseCode >= ERROR_STATUS) { @@ -60,4 +61,24 @@ public static void checkError(final InternalSerde util, final InternalResponse r } } } + + public static ArangoDBException translateError(final InternalSerde util, final InternalResponse response) { + final int responseCode = response.getResponseCode(); + if (responseCode >= ERROR_STATUS) { + if (responseCode == ERROR_INTERNAL && response.containsMeta(HEADER_ENDPOINT)) { + return new ArangoDBRedirectException(String.format("Response Code: %s", responseCode), + response.getMeta(HEADER_ENDPOINT)); + } else if (response.getBody() != null) { + final ErrorEntity errorEntity = util.deserialize(response.getBody(), ErrorEntity.class); + ArangoDBException e = new ArangoDBException(errorEntity); + if (ArangoErrors.QUEUE_TIME_VIOLATED.equals(e.getErrorNum())) { + return new ArangoDBException(new TimeoutException().initCause(e)); + } + return e; + } else { + return new ArangoDBException(String.format("Response Code: %s", responseCode), responseCode); + } + } + return null; + } } diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index 2a522d71f..153553187 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -37,12 +37,14 @@ import java.io.Closeable; import java.io.IOException; import java.net.SocketTimeoutException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; /** * @author Mark Vollmary + * @author Michele Rastelli */ public class HttpCommunication implements Closeable { @@ -64,76 +66,101 @@ public void close() throws IOException { } public InternalResponse execute(final InternalRequest request, final HostHandle hostHandle) { - return execute(request, hostHandle, 0); + try { + return executeAsync(request, hostHandle, 0).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw ArangoDBException.wrap(e); + } catch (ExecutionException e) { + throw ArangoDBException.wrap(e.getCause()); + } } - private InternalResponse execute(final InternalRequest request, final HostHandle hostHandle, final int attemptCount) { + private CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle, final int attemptCount) { + final CompletableFuture rfuture = new CompletableFuture<>(); final AccessType accessType = RequestUtils.determineAccessType(request); Host host = hostHandler.get(hostHandle, accessType); try { while (true) { long reqId = reqCount.getAndIncrement(); - try { - final HttpConnection connection = (HttpConnection) host.connection(); - if (LOGGER.isDebugEnabled()) { - String body = request.getBody() == null ? "" : serde.toJsonString(request.getBody()); - LOGGER.debug("Send Request [id={}]: {} {}", reqId, request, body); - } - final InternalResponse response; - try { - response = connection.executeAsync(request).get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw ArangoDBException.wrap(e); - } catch (ExecutionException e) { - throw ArangoDBException.wrap(e.getCause()); - } - - if (LOGGER.isDebugEnabled()) { - String body = response.getBody() == null ? "" : serde.toJsonString(response.getBody()); - LOGGER.debug("Received Response [id={}]: {} {}", reqId, response, body); - } - ResponseUtils.checkError(serde, response); - hostHandler.success(); - hostHandler.confirm(); - return response; - } catch (final ArangoDBException e) { - Throwable cause = e.getCause(); - if (cause instanceof SocketTimeoutException) { - // SocketTimeoutException exceptions are wrapped and rethrown. - TimeoutException te = new TimeoutException(cause.getMessage()); - te.initCause(cause); - throw new ArangoDBException(te, reqId); - } else if (cause instanceof IOException) { - hostHandler.fail((IOException) cause); - if (hostHandle != null && hostHandle.getHost() != null) { - hostHandle.setHost(null); - } - final Host failedHost = host; - host = hostHandler.get(hostHandle, accessType); - if (host != null && isSafe(request)) { - LOGGER.warn("Could not connect to {} while executing request [id={}]", - failedHost.getDescription(), reqId, cause); - LOGGER.debug("Try connecting to {}", host.getDescription()); - } else { - LOGGER.error(cause.getMessage(), cause); - throw new ArangoDBException(cause, reqId); - } - } else { - throw e; - } + final HttpConnection connection = (HttpConnection) host.connection(); + if (LOGGER.isDebugEnabled()) { + String body = request.getBody() == null ? "" : serde.toJsonString(request.getBody()); + LOGGER.debug("Send Request [id={}]: {} {}", reqId, request, body); } + connection.executeAsync(request) + .whenComplete((response, e) -> { + if (e != null) { + rfuture.completeExceptionally(mapException(e, reqId, hostHandle)); + } else { + if (LOGGER.isDebugEnabled()) { + String body = response.getBody() == null ? "" : serde.toJsonString(response.getBody()); + LOGGER.debug("Received Response [id={}]: {} {}", reqId, response, body); + } + ArangoDBException errorEntityEx = ResponseUtils.translateError(serde, response); + if (errorEntityEx != null) { + rfuture.completeExceptionally(errorEntityEx); + } else { + hostHandler.success(); + hostHandler.confirm(); + rfuture.complete(response); + } + } + }); + break; } } catch (final ArangoDBRedirectException e) { if (attemptCount < 3) { final String location = e.getLocation(); final HostDescription redirectHost = HostUtils.createFromLocation(location); hostHandler.failIfNotMatch(redirectHost, e); - return execute(request, new HostHandle().setHost(redirectHost), attemptCount + 1); + return executeAsync(request, new HostHandle().setHost(redirectHost), attemptCount + 1); } else { throw e; } } + + return rfuture; + } + + private ArangoDBException mapException(Throwable e, long reqId, HostHandle hostHandle) { + if (e instanceof SocketTimeoutException) { + // SocketTimeoutException exceptions are wrapped and rethrown. + TimeoutException te = new TimeoutException(e.getMessage()); + te.initCause(e); + return new ArangoDBException(te, reqId); + } + + if (e instanceof TimeoutException) { + return new ArangoDBException(e, reqId); + } + + IOException ioEx = wrapIOEx(e); + hostHandler.fail(ioEx); + if (hostHandle != null && hostHandle.getHost() != null) { + hostHandle.setHost(null); + } + // FIXME +// final Host failedHost = host; +// host = hostHandler.get(hostHandle, accessType); +// if (host != null && isSafe(request)) { +// LOGGER.warn("Could not connect to {} while executing request [id={}]", +// failedHost.getDescription(), reqId, cause); +// LOGGER.debug("Try connecting to {}", host.getDescription()); +// } else { + + ArangoDBException aEx = new ArangoDBException(ioEx, reqId); + LOGGER.error(aEx.getMessage(), aEx); + return aEx; +// } + } + + private static IOException wrapIOEx(Throwable t) { + if (t instanceof IOException) { + return (IOException) t; + } else { + return new IOException(t); + } } private boolean isSafe(final InternalRequest request) { diff --git a/http/src/main/java/com/arangodb/http/HttpConnection.java b/http/src/main/java/com/arangodb/http/HttpConnection.java index cea75ab38..3bfd5b393 100644 --- a/http/src/main/java/com/arangodb/http/HttpConnection.java +++ b/http/src/main/java/com/arangodb/http/HttpConnection.java @@ -20,15 +20,18 @@ package com.arangodb.http; -import com.arangodb.*; +import com.arangodb.ArangoDBException; +import com.arangodb.ContentType; +import com.arangodb.PackageVersion; +import com.arangodb.Protocol; import com.arangodb.config.HostDescription; +import com.arangodb.internal.InternalRequest; +import com.arangodb.internal.InternalResponse; +import com.arangodb.internal.RequestType; import com.arangodb.internal.config.ArangoConfig; import com.arangodb.internal.net.Connection; import com.arangodb.internal.serde.ContentTypeFactory; import com.arangodb.internal.util.EncodeUtils; -import com.arangodb.internal.InternalRequest; -import com.arangodb.internal.RequestType; -import com.arangodb.internal.InternalResponse; import io.netty.handler.ssl.ApplicationProtocolConfig; import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.IdentityCipherSuiteFilter; @@ -51,14 +54,12 @@ import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; -import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; @@ -223,18 +224,7 @@ private HttpMethod requestTypeToHttpMethod(RequestType requestType) { public CompletableFuture executeAsync(final InternalRequest request) { CompletableFuture rfuture = new CompletableFuture<>(); vertx.runOnContext(e -> doExecute(request, rfuture)); - return rfuture.handle((r, e) -> { - if (e instanceof TimeoutException) { - throw new ArangoDBException(e); - } - if (e instanceof IOException) { - throw new ArangoDBException(e); - } - if (e != null) { - throw new ArangoDBException(new IOException(e)); - } - return r; - }); + return rfuture; } public void doExecute(final InternalRequest request, final CompletableFuture rfuture) { From d8e484d09519f894a2261807f639cddd1b8642cf Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 10 Oct 2023 14:04:27 +0200 Subject: [PATCH 08/62] HttpConnection async refactoring --- .../com/arangodb/http/HttpCommunication.java | 151 ++++++++++-------- 1 file changed, 81 insertions(+), 70 deletions(-) diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index 153553187..027e42191 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -67,7 +67,7 @@ public void close() throws IOException { public InternalResponse execute(final InternalRequest request, final HostHandle hostHandle) { try { - return executeAsync(request, hostHandle, 0).get(); + return executeAsync(request, hostHandle, hostHandler.get(hostHandle, RequestUtils.determineAccessType(request)), 0).get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw ArangoDBException.wrap(e); @@ -76,85 +76,96 @@ public InternalResponse execute(final InternalRequest request, final HostHandle } } - private CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle, final int attemptCount) { + private CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle, final Host host, final int attemptCount) { final CompletableFuture rfuture = new CompletableFuture<>(); final AccessType accessType = RequestUtils.determineAccessType(request); - Host host = hostHandler.get(hostHandle, accessType); - try { - while (true) { - long reqId = reqCount.getAndIncrement(); - final HttpConnection connection = (HttpConnection) host.connection(); - if (LOGGER.isDebugEnabled()) { - String body = request.getBody() == null ? "" : serde.toJsonString(request.getBody()); - LOGGER.debug("Send Request [id={}]: {} {}", reqId, request, body); - } - connection.executeAsync(request) - .whenComplete((response, e) -> { - if (e != null) { - rfuture.completeExceptionally(mapException(e, reqId, hostHandle)); + long reqId = reqCount.getAndIncrement(); + final HttpConnection connection = (HttpConnection) host.connection(); + if (LOGGER.isDebugEnabled()) { + String body = request.getBody() == null ? "" : serde.toJsonString(request.getBody()); + LOGGER.debug("Send Request [id={}]: {} {}", reqId, request, body); + } + connection.executeAsync(request) + .whenComplete((response, e) -> { + try { + if (e instanceof SocketTimeoutException) { + // SocketTimeoutException exceptions are wrapped and rethrown. + TimeoutException te = new TimeoutException(e.getMessage()); + te.initCause(e); + rfuture.completeExceptionally(new ArangoDBException(te, reqId)); + } else if (e instanceof TimeoutException) { + rfuture.completeExceptionally(new ArangoDBException(e, reqId)); + } else if (e != null) { + IOException ioEx = wrapIOEx(e); + hostHandler.fail(ioEx); + if (hostHandle != null && hostHandle.getHost() != null) { + hostHandle.setHost(null); + } + + Host nextHost; + try { + nextHost = hostHandler.get(hostHandle, accessType); + } catch (ArangoDBException aEx) { + rfuture.completeExceptionally(aEx); + return; + } + + if (nextHost != null && isSafe(request)) { + LOGGER.warn("Could not connect to {} while executing request [id={}]", + host.getDescription(), reqId, ioEx); + LOGGER.debug("Try connecting to {}", nextHost.getDescription()); + executeAsync(request, hostHandle, nextHost, attemptCount) + .whenComplete((v, err) -> { + if (err != null) { + rfuture.completeExceptionally(err); + } else { + rfuture.complete(v); + } + }); } else { - if (LOGGER.isDebugEnabled()) { - String body = response.getBody() == null ? "" : serde.toJsonString(response.getBody()); - LOGGER.debug("Received Response [id={}]: {} {}", reqId, response, body); - } - ArangoDBException errorEntityEx = ResponseUtils.translateError(serde, response); - if (errorEntityEx != null) { + ArangoDBException aEx = new ArangoDBException(ioEx, reqId); + LOGGER.error(aEx.getMessage(), aEx); + rfuture.completeExceptionally(aEx); + } + } else { + if (LOGGER.isDebugEnabled()) { + String body = response.getBody() == null ? "" : serde.toJsonString(response.getBody()); + LOGGER.debug("Received Response [id={}]: {} {}", reqId, response, body); + } + ArangoDBException errorEntityEx = ResponseUtils.translateError(serde, response); + if (errorEntityEx instanceof ArangoDBRedirectException) { + if (attemptCount >= 3) { rfuture.completeExceptionally(errorEntityEx); } else { - hostHandler.success(); - hostHandler.confirm(); - rfuture.complete(response); + final String location = ((ArangoDBRedirectException) errorEntityEx).getLocation(); + final HostDescription redirectHost = HostUtils.createFromLocation(location); + hostHandler.failIfNotMatch(redirectHost, errorEntityEx); + executeAsync(request, new HostHandle().setHost(redirectHost), hostHandler.get(hostHandle, accessType), attemptCount + 1) + .whenComplete((v, err) -> { + if (err != null) { + rfuture.completeExceptionally(err); + } else { + rfuture.complete(v); + } + }); } + } else if (errorEntityEx != null) { + rfuture.completeExceptionally(errorEntityEx); + } else { + hostHandler.success(); + hostHandler.confirm(); + rfuture.complete(response); } - }); - break; - } - } catch (final ArangoDBRedirectException e) { - if (attemptCount < 3) { - final String location = e.getLocation(); - final HostDescription redirectHost = HostUtils.createFromLocation(location); - hostHandler.failIfNotMatch(redirectHost, e); - return executeAsync(request, new HostHandle().setHost(redirectHost), attemptCount + 1); - } else { - throw e; - } - } - + } + } catch (Exception ex) { + // FIXME: convert to handle() block + LOGGER.error("FATAL: Unhandled exception", ex); + System.exit(1); + } + }); return rfuture; } - private ArangoDBException mapException(Throwable e, long reqId, HostHandle hostHandle) { - if (e instanceof SocketTimeoutException) { - // SocketTimeoutException exceptions are wrapped and rethrown. - TimeoutException te = new TimeoutException(e.getMessage()); - te.initCause(e); - return new ArangoDBException(te, reqId); - } - - if (e instanceof TimeoutException) { - return new ArangoDBException(e, reqId); - } - - IOException ioEx = wrapIOEx(e); - hostHandler.fail(ioEx); - if (hostHandle != null && hostHandle.getHost() != null) { - hostHandle.setHost(null); - } - // FIXME -// final Host failedHost = host; -// host = hostHandler.get(hostHandle, accessType); -// if (host != null && isSafe(request)) { -// LOGGER.warn("Could not connect to {} while executing request [id={}]", -// failedHost.getDescription(), reqId, cause); -// LOGGER.debug("Try connecting to {}", host.getDescription()); -// } else { - - ArangoDBException aEx = new ArangoDBException(ioEx, reqId); - LOGGER.error(aEx.getMessage(), aEx); - return aEx; -// } - } - private static IOException wrapIOEx(Throwable t) { if (t instanceof IOException) { return (IOException) t; From 95454d3f3cf0d927eb16ce88a7eb5d86cc49bebe Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 10 Oct 2023 14:27:09 +0200 Subject: [PATCH 09/62] HttpConnection async refactoring --- .../com/arangodb/http/HttpCommunication.java | 48 ++++++++----------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index 027e42191..7a3dba190 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -78,7 +78,6 @@ public InternalResponse execute(final InternalRequest request, final HostHandle private CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle, final Host host, final int attemptCount) { final CompletableFuture rfuture = new CompletableFuture<>(); - final AccessType accessType = RequestUtils.determineAccessType(request); long reqId = reqCount.getAndIncrement(); final HttpConnection connection = (HttpConnection) host.connection(); if (LOGGER.isDebugEnabled()) { @@ -102,26 +101,15 @@ private CompletableFuture executeAsync(final InternalRequest r hostHandle.setHost(null); } - Host nextHost; - try { - nextHost = hostHandler.get(hostHandle, accessType); - } catch (ArangoDBException aEx) { - rfuture.completeExceptionally(aEx); - return; - } - + Host nextHost = hostHandler.get(hostHandle, RequestUtils.determineAccessType(request)); if (nextHost != null && isSafe(request)) { LOGGER.warn("Could not connect to {} while executing request [id={}]", host.getDescription(), reqId, ioEx); LOGGER.debug("Try connecting to {}", nextHost.getDescription()); - executeAsync(request, hostHandle, nextHost, attemptCount) - .whenComplete((v, err) -> { - if (err != null) { - rfuture.completeExceptionally(err); - } else { - rfuture.complete(v); - } - }); + mirror( + executeAsync(request, hostHandle, nextHost, attemptCount), + rfuture + ); } else { ArangoDBException aEx = new ArangoDBException(ioEx, reqId); LOGGER.error(aEx.getMessage(), aEx); @@ -140,14 +128,10 @@ private CompletableFuture executeAsync(final InternalRequest r final String location = ((ArangoDBRedirectException) errorEntityEx).getLocation(); final HostDescription redirectHost = HostUtils.createFromLocation(location); hostHandler.failIfNotMatch(redirectHost, errorEntityEx); - executeAsync(request, new HostHandle().setHost(redirectHost), hostHandler.get(hostHandle, accessType), attemptCount + 1) - .whenComplete((v, err) -> { - if (err != null) { - rfuture.completeExceptionally(err); - } else { - rfuture.complete(v); - } - }); + mirror( + executeAsync(request, new HostHandle().setHost(redirectHost), hostHandler.get(hostHandle, RequestUtils.determineAccessType(request)), attemptCount + 1), + rfuture + ); } } else if (errorEntityEx != null) { rfuture.completeExceptionally(errorEntityEx); @@ -158,14 +142,22 @@ private CompletableFuture executeAsync(final InternalRequest r } } } catch (Exception ex) { - // FIXME: convert to handle() block - LOGGER.error("FATAL: Unhandled exception", ex); - System.exit(1); + rfuture.completeExceptionally(ex); } }); return rfuture; } + private void mirror(CompletableFuture up, CompletableFuture down) { + up.whenComplete((v, err) -> { + if (err != null) { + down.completeExceptionally(err); + } else { + down.complete(v); + } + }); + } + private static IOException wrapIOEx(Throwable t) { if (t instanceof IOException) { return (IOException) t; From df9e5f50f3aa60e0d2c9d6540a17e56451b03436 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 10 Oct 2023 14:40:55 +0200 Subject: [PATCH 10/62] HttpConnection async refactoring --- .../internal/net/CommunicationProtocol.java | 16 +++++++++++- .../com/arangodb/http/HttpCommunication.java | 19 ++++++-------- .../java/com/arangodb/http/HttpProtocol.java | 25 +++++++++++++++---- .../java/com/arangodb/vst/VstProtocol.java | 7 ++++++ 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java b/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java index a7f5d2b75..e35f917cc 100644 --- a/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java +++ b/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java @@ -20,17 +20,31 @@ package com.arangodb.internal.net; +import com.arangodb.ArangoDBException; import com.arangodb.internal.InternalRequest; import com.arangodb.internal.InternalResponse; import java.io.Closeable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; /** * @author Mark Vollmary */ public interface CommunicationProtocol extends Closeable { - InternalResponse execute(final InternalRequest request, HostHandle hostHandle); + default InternalResponse execute(final InternalRequest request, final HostHandle hostHandle) { + try { + return executeAsync(request, hostHandle).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw ArangoDBException.wrap(e); + } catch (ExecutionException e) { + throw ArangoDBException.wrap(e.getCause()); + } + } + + CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle); void setJwt(String jwt); diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index 7a3dba190..beab69741 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -23,10 +23,13 @@ import com.arangodb.ArangoDBException; import com.arangodb.config.HostDescription; import com.arangodb.internal.InternalRequest; -import com.arangodb.internal.RequestType; import com.arangodb.internal.InternalResponse; +import com.arangodb.internal.RequestType; import com.arangodb.internal.config.ArangoConfig; -import com.arangodb.internal.net.*; +import com.arangodb.internal.net.ArangoDBRedirectException; +import com.arangodb.internal.net.Host; +import com.arangodb.internal.net.HostHandle; +import com.arangodb.internal.net.HostHandler; import com.arangodb.internal.serde.InternalSerde; import com.arangodb.internal.util.HostUtils; import com.arangodb.internal.util.RequestUtils; @@ -38,7 +41,6 @@ import java.io.IOException; import java.net.SocketTimeoutException; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; @@ -65,15 +67,8 @@ public void close() throws IOException { hostHandler.close(); } - public InternalResponse execute(final InternalRequest request, final HostHandle hostHandle) { - try { - return executeAsync(request, hostHandle, hostHandler.get(hostHandle, RequestUtils.determineAccessType(request)), 0).get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw ArangoDBException.wrap(e); - } catch (ExecutionException e) { - throw ArangoDBException.wrap(e.getCause()); - } + public CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle) { + return executeAsync(request, hostHandle, hostHandler.get(hostHandle, RequestUtils.determineAccessType(request)), 0); } private CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle, final Host host, final int attemptCount) { diff --git a/http/src/main/java/com/arangodb/http/HttpProtocol.java b/http/src/main/java/com/arangodb/http/HttpProtocol.java index a78f5487f..86fd47ca2 100644 --- a/http/src/main/java/com/arangodb/http/HttpProtocol.java +++ b/http/src/main/java/com/arangodb/http/HttpProtocol.java @@ -20,28 +20,43 @@ package com.arangodb.http; +import com.arangodb.ArangoDBException; import com.arangodb.internal.net.CommunicationProtocol; import com.arangodb.internal.net.HostHandle; import com.arangodb.internal.InternalRequest; import com.arangodb.internal.InternalResponse; import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; /** * @author Mark Vollmary */ public class HttpProtocol implements CommunicationProtocol { - private final HttpCommunication httpCommunitaction; + private final HttpCommunication httpCommunication; - public HttpProtocol(final HttpCommunication httpCommunitaction) { + public HttpProtocol(final HttpCommunication httpCommunication) { super(); - this.httpCommunitaction = httpCommunitaction; + this.httpCommunication = httpCommunication; } @Override public InternalResponse execute(final InternalRequest request, final HostHandle hostHandle) { - return httpCommunitaction.execute(request, hostHandle); + try { + return executeAsync(request, hostHandle).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw ArangoDBException.wrap(e); + } catch (ExecutionException e) { + throw ArangoDBException.wrap(e.getCause()); + } + } + + @Override + public CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle) { + return httpCommunication.executeAsync(request, hostHandle); } @Override @@ -51,7 +66,7 @@ public void setJwt(String jwt) { @Override public void close() throws IOException { - httpCommunitaction.close(); + httpCommunication.close(); } } diff --git a/vst/src/main/java/com/arangodb/vst/VstProtocol.java b/vst/src/main/java/com/arangodb/vst/VstProtocol.java index 3642f1826..76fe16b1b 100644 --- a/vst/src/main/java/com/arangodb/vst/VstProtocol.java +++ b/vst/src/main/java/com/arangodb/vst/VstProtocol.java @@ -27,6 +27,7 @@ import com.arangodb.vst.internal.VstConnectionSync; import java.io.IOException; +import java.util.concurrent.CompletableFuture; /** * @author Mark Vollmary @@ -45,6 +46,12 @@ public InternalResponse execute(final InternalRequest request, final HostHandle return communication.execute(request, hostHandle); } + @Override + public CompletableFuture executeAsync(InternalRequest request, HostHandle hostHandle) { + // FIXME + throw new UnsupportedOperationException(); + } + @Override public void setJwt(String jwt) { communication.setJwt(jwt); From 3355765db87508a17a4966ddead98f3a1b09c4ff Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 10 Oct 2023 21:42:49 +0200 Subject: [PATCH 11/62] VST async refactoring --- .../java/com/arangodb/http/HttpProtocol.java | 18 +-- .../vstKeepAlive/VstKeepAliveCloseTest.java | 1 + .../arangodb/vst/VstCommunicationAsync.java | 146 ++++++++++++++++++ .../arangodb/vst/VstCommunicationSync.java | 101 ------------ ...nc.java => VstConnectionFactoryAsync.java} | 7 +- .../java/com/arangodb/vst/VstProtocol.java | 17 +- .../com/arangodb/vst/VstProtocolProvider.java | 4 +- ...ctionSync.java => VstConnectionAsync.java} | 35 +++-- .../utils/CompletableFutureUtils.java | 24 +++ 9 files changed, 203 insertions(+), 150 deletions(-) create mode 100644 vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java delete mode 100644 vst/src/main/java/com/arangodb/vst/VstCommunicationSync.java rename vst/src/main/java/com/arangodb/vst/{VstConnectionFactorySync.java => VstConnectionFactoryAsync.java} (85%) rename vst/src/main/java/com/arangodb/vst/internal/{VstConnectionSync.java => VstConnectionAsync.java} (57%) create mode 100644 vst/src/main/java/com/arangodb/vst/internal/utils/CompletableFutureUtils.java diff --git a/http/src/main/java/com/arangodb/http/HttpProtocol.java b/http/src/main/java/com/arangodb/http/HttpProtocol.java index 86fd47ca2..d3df5cd5b 100644 --- a/http/src/main/java/com/arangodb/http/HttpProtocol.java +++ b/http/src/main/java/com/arangodb/http/HttpProtocol.java @@ -20,15 +20,13 @@ package com.arangodb.http; -import com.arangodb.ArangoDBException; -import com.arangodb.internal.net.CommunicationProtocol; -import com.arangodb.internal.net.HostHandle; import com.arangodb.internal.InternalRequest; import com.arangodb.internal.InternalResponse; +import com.arangodb.internal.net.CommunicationProtocol; +import com.arangodb.internal.net.HostHandle; import java.io.IOException; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; /** * @author Mark Vollmary @@ -42,18 +40,6 @@ public HttpProtocol(final HttpCommunication httpCommunication) { this.httpCommunication = httpCommunication; } - @Override - public InternalResponse execute(final InternalRequest request, final HostHandle hostHandle) { - try { - return executeAsync(request, hostHandle).get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw ArangoDBException.wrap(e); - } catch (ExecutionException e) { - throw ArangoDBException.wrap(e.getCause()); - } - } - @Override public CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle) { return httpCommunication.executeAsync(request, hostHandle); diff --git a/resilience-tests/src/test/java/resilience/vstKeepAlive/VstKeepAliveCloseTest.java b/resilience-tests/src/test/java/resilience/vstKeepAlive/VstKeepAliveCloseTest.java index c8a09bf1a..2f0574651 100644 --- a/resilience-tests/src/test/java/resilience/vstKeepAlive/VstKeepAliveCloseTest.java +++ b/resilience-tests/src/test/java/resilience/vstKeepAlive/VstKeepAliveCloseTest.java @@ -48,6 +48,7 @@ void keepAliveCloseAndReconnect() throws IOException { Latency toxic = getEndpoint().getProxy().toxics().latency("latency", ToxicDirection.DOWNSTREAM, 10_000); await().until(() -> logs.getLoggedEvents().stream() .filter(e -> e.getLevel().equals(Level.ERROR)) + .filter(e -> e.getMessage() != null) .anyMatch(e -> e.getMessage().contains("Connection unresponsive!"))); toxic.setLatency(0); arangoDB.getVersion(); diff --git a/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java b/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java new file mode 100644 index 000000000..07850d95e --- /dev/null +++ b/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java @@ -0,0 +1,146 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.vst; + +import com.arangodb.ArangoDBException; +import com.arangodb.config.HostDescription; +import com.arangodb.internal.InternalRequest; +import com.arangodb.internal.InternalResponse; +import com.arangodb.internal.config.ArangoConfig; +import com.arangodb.internal.net.ArangoDBRedirectException; +import com.arangodb.internal.net.HostHandle; +import com.arangodb.internal.net.HostHandler; +import com.arangodb.internal.util.HostUtils; +import com.arangodb.velocypack.exception.VPackException; +import com.arangodb.velocypack.exception.VPackParserException; +import com.arangodb.vst.internal.AuthenticationRequest; +import com.arangodb.vst.internal.JwtAuthenticationRequest; +import com.arangodb.vst.internal.Message; +import com.arangodb.vst.internal.VstConnectionAsync; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +/** + * @author Mark Vollmary + */ +public class VstCommunicationAsync extends VstCommunication, VstConnectionAsync> { + + private static final Logger LOGGER = LoggerFactory.getLogger(VstCommunicationAsync.class); + + public VstCommunicationAsync(final ArangoConfig config, final HostHandler hostHandler) { + super(config, hostHandler); + } + + @Override + protected CompletableFuture execute(final InternalRequest request, final VstConnectionAsync connection) { + return execute(request, connection, 0); + } + + @Override + protected CompletableFuture execute(final InternalRequest request, final VstConnectionAsync connection, final int attemptCount) { + final CompletableFuture rfuture = new CompletableFuture<>(); + try { + final Message message = createMessage(request); + send(message, connection).whenComplete((m, ex) -> { + if (m != null) { + final InternalResponse response; + try { + response = createResponse(m); + } catch (final VPackParserException e) { + LOGGER.error(e.getMessage(), e); + rfuture.completeExceptionally(e); + return; + } + + try { + checkError(response); + } catch (final ArangoDBRedirectException e) { + if (attemptCount >= 3) { + rfuture.completeExceptionally(e); + return; + } + final String location = e.getLocation(); + final HostDescription redirectHost = HostUtils.createFromLocation(location); + hostHandler.failIfNotMatch(redirectHost, e); + execute(request, new HostHandle().setHost(redirectHost), attemptCount + 1) + .whenComplete((v, err) -> { + if (v != null) { + rfuture.complete(v); + } else if (err != null) { + rfuture.completeExceptionally(err); + } else { + rfuture.cancel(true); + } + }); + return; + } catch (ArangoDBException e) { + rfuture.completeExceptionally(e); + } + rfuture.complete(response); + } else if (ex != null) { + LOGGER.error(ex.getMessage(), ex); + rfuture.completeExceptionally(ex); + } else { + rfuture.cancel(true); + } + }); + } catch (final VPackException e) { + LOGGER.error(e.getMessage(), e); + rfuture.completeExceptionally(e); + } + return rfuture; + } + + private CompletableFuture send(final Message message, final VstConnectionAsync connection) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("Send Message (id=%s, head=%s, body=%s)", message.getId(), message.getHead(), + message.getBody() != null ? message.getBody() : "{}")); + } + return connection.write(message, buildChunks(message)); + } + + @Override + protected void authenticate(final VstConnectionAsync connection) { + InternalRequest authRequest; + if (jwt != null) { + authRequest = new JwtAuthenticationRequest(jwt, ENCRYPTION_JWT); + } else { + authRequest = new AuthenticationRequest(user, password != null ? password : "", ENCRYPTION_PLAIN); + } + + InternalResponse response; + try { + response = execute(authRequest, connection).get(); + } catch (final InterruptedException | ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof ArangoDBException) { + throw (ArangoDBException) cause; + } else { + throw new ArangoDBException(e.getCause()); + } + } + checkError(response); + } + +} diff --git a/vst/src/main/java/com/arangodb/vst/VstCommunicationSync.java b/vst/src/main/java/com/arangodb/vst/VstCommunicationSync.java deleted file mode 100644 index 97b7671c7..000000000 --- a/vst/src/main/java/com/arangodb/vst/VstCommunicationSync.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * DISCLAIMER - * - * Copyright 2016 ArangoDB GmbH, Cologne, Germany - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright holder is ArangoDB GmbH, Cologne, Germany - */ - -package com.arangodb.vst; - -import com.arangodb.ArangoDBException; -import com.arangodb.config.HostDescription; -import com.arangodb.internal.InternalRequest; -import com.arangodb.internal.InternalResponse; -import com.arangodb.internal.config.ArangoConfig; -import com.arangodb.internal.net.ArangoDBRedirectException; -import com.arangodb.internal.net.HostHandle; -import com.arangodb.internal.net.HostHandler; -import com.arangodb.internal.util.HostUtils; -import com.arangodb.velocypack.exception.VPackParserException; -import com.arangodb.vst.internal.AuthenticationRequest; -import com.arangodb.vst.internal.JwtAuthenticationRequest; -import com.arangodb.vst.internal.Message; -import com.arangodb.vst.internal.VstConnectionSync; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Mark Vollmary - */ -public class VstCommunicationSync extends VstCommunication { - - private static final Logger LOGGER = LoggerFactory.getLogger(VstCommunicationSync.class); - - public VstCommunicationSync(final ArangoConfig config, final HostHandler hostHandler) { - super(config, hostHandler); - } - - @Override - protected InternalResponse execute(final InternalRequest request, final VstConnectionSync connection) { - return execute(request, connection, 0); - } - - @Override - protected InternalResponse execute(final InternalRequest request, final VstConnectionSync connection, final int attemptCount) { - try { - final Message requestMessage = createMessage(request); - if (LOGGER.isDebugEnabled()) { - String body = request.getBody() == null ? "" : serde.toJsonString(request.getBody()); - LOGGER.debug("Send Request [id={}]: {} {}", requestMessage.getId(), request, body); - } - final Message responseMessage = send(requestMessage, connection); - final InternalResponse response = createResponse(responseMessage); - if (LOGGER.isDebugEnabled()) { - String body = response.getBody() == null ? "" : serde.toJsonString(response.getBody()); - LOGGER.debug("Received Response [id={}]: {} {}", responseMessage.getId(), response, body); - } - checkError(response); - return response; - } catch (final VPackParserException e) { - throw new ArangoDBException(e); - } catch (final ArangoDBRedirectException e) { - if (attemptCount >= 3) { - throw e; - } - final String location = e.getLocation(); - final HostDescription redirectHost = HostUtils.createFromLocation(location); - hostHandler.failIfNotMatch(redirectHost, e); - return execute(request, new HostHandle().setHost(redirectHost), attemptCount + 1); - } - } - - private Message send(final Message message, final VstConnectionSync connection) { - return connection.write(message, buildChunks(message)); - } - - @Override - protected void authenticate(final VstConnectionSync connection) { - InternalRequest authRequest; - if (jwt != null) { - authRequest = new JwtAuthenticationRequest(jwt, ENCRYPTION_JWT); - } else { - authRequest = new AuthenticationRequest(user, password != null ? password : "", ENCRYPTION_PLAIN); - } - final InternalResponse response = execute(authRequest, connection); - checkError(response); - } - -} diff --git a/vst/src/main/java/com/arangodb/vst/VstConnectionFactorySync.java b/vst/src/main/java/com/arangodb/vst/VstConnectionFactoryAsync.java similarity index 85% rename from vst/src/main/java/com/arangodb/vst/VstConnectionFactorySync.java rename to vst/src/main/java/com/arangodb/vst/VstConnectionFactoryAsync.java index 0ab5860aa..2162b871b 100644 --- a/vst/src/main/java/com/arangodb/vst/VstConnectionFactorySync.java +++ b/vst/src/main/java/com/arangodb/vst/VstConnectionFactoryAsync.java @@ -24,15 +24,16 @@ import com.arangodb.internal.config.ArangoConfig; import com.arangodb.internal.net.Connection; import com.arangodb.internal.net.ConnectionFactory; -import com.arangodb.vst.internal.VstConnectionSync; +import com.arangodb.vst.internal.VstConnectionAsync; /** * @author Mark Vollmary */ -public class VstConnectionFactorySync implements ConnectionFactory { +public class VstConnectionFactoryAsync implements ConnectionFactory { @Override public Connection create(final ArangoConfig config, final HostDescription host) { - return new VstConnectionSync(config, host); + return new VstConnectionAsync(config, host); } + } diff --git a/vst/src/main/java/com/arangodb/vst/VstProtocol.java b/vst/src/main/java/com/arangodb/vst/VstProtocol.java index 76fe16b1b..750308b5d 100644 --- a/vst/src/main/java/com/arangodb/vst/VstProtocol.java +++ b/vst/src/main/java/com/arangodb/vst/VstProtocol.java @@ -20,11 +20,10 @@ package com.arangodb.vst; -import com.arangodb.internal.net.CommunicationProtocol; -import com.arangodb.internal.net.HostHandle; import com.arangodb.internal.InternalRequest; import com.arangodb.internal.InternalResponse; -import com.arangodb.vst.internal.VstConnectionSync; +import com.arangodb.internal.net.CommunicationProtocol; +import com.arangodb.internal.net.HostHandle; import java.io.IOException; import java.util.concurrent.CompletableFuture; @@ -34,22 +33,16 @@ */ public class VstProtocol implements CommunicationProtocol { - private final VstCommunication communication; + private final VstCommunicationAsync communication; - public VstProtocol(final VstCommunication communication) { + public VstProtocol(final VstCommunicationAsync communication) { super(); this.communication = communication; } - @Override - public InternalResponse execute(final InternalRequest request, final HostHandle hostHandle) { - return communication.execute(request, hostHandle); - } - @Override public CompletableFuture executeAsync(InternalRequest request, HostHandle hostHandle) { - // FIXME - throw new UnsupportedOperationException(); + return communication.execute(request, hostHandle); } @Override diff --git a/vst/src/main/java/com/arangodb/vst/VstProtocolProvider.java b/vst/src/main/java/com/arangodb/vst/VstProtocolProvider.java index 8a6341fc7..6d8eb0fa5 100644 --- a/vst/src/main/java/com/arangodb/vst/VstProtocolProvider.java +++ b/vst/src/main/java/com/arangodb/vst/VstProtocolProvider.java @@ -16,12 +16,12 @@ public boolean supportsProtocol(Protocol protocol) { @Override public ConnectionFactory createConnectionFactory() { - return new VstConnectionFactorySync(); + return new VstConnectionFactoryAsync(); } @Override public CommunicationProtocol createProtocol(ArangoConfig config, HostHandler hostHandler) { - return new VstProtocol(new VstCommunicationSync(config, hostHandler)); + return new VstProtocol(new VstCommunicationAsync(config, hostHandler)); } @Override diff --git a/vst/src/main/java/com/arangodb/vst/internal/VstConnectionSync.java b/vst/src/main/java/com/arangodb/vst/internal/VstConnectionAsync.java similarity index 57% rename from vst/src/main/java/com/arangodb/vst/internal/VstConnectionSync.java rename to vst/src/main/java/com/arangodb/vst/internal/VstConnectionAsync.java index 28a8bcd89..ace6e21a7 100644 --- a/vst/src/main/java/com/arangodb/vst/internal/VstConnectionSync.java +++ b/vst/src/main/java/com/arangodb/vst/internal/VstConnectionAsync.java @@ -20,44 +20,47 @@ package com.arangodb.vst.internal; -import com.arangodb.ArangoDBException; import com.arangodb.config.HostDescription; import com.arangodb.internal.config.ArangoConfig; +import com.arangodb.vst.internal.utils.CompletableFutureUtils; import java.util.Collection; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; /** * @author Mark Vollmary */ -public class VstConnectionSync extends VstConnection { +public class VstConnectionAsync extends VstConnection> { - public VstConnectionSync(final ArangoConfig config, final HostDescription host) { + public VstConnectionAsync(final ArangoConfig config, final HostDescription host) { super(config, host); } @Override - public Message write(final Message message, final Collection chunks) { - final FutureTask task = new FutureTask<>(() -> messageStore.get(message.getId())); + public synchronized CompletableFuture write(final Message message, final Collection chunks) { + final CompletableFuture future = new CompletableFuture<>(); + final FutureTask task = new FutureTask<>(() -> { + try { + future.complete(messageStore.get(message.getId())); + } catch (final Exception e) { + future.completeExceptionally(e); + } + return null; + }); messageStore.storeMessage(message.getId(), task); super.writeIntern(message, chunks); - try { - return timeout == null || timeout == 0L ? task.get() : task.get(timeout, TimeUnit.MILLISECONDS); - } catch (final ExecutionException e) { - throw ArangoDBException.wrap(e.getCause()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new ArangoDBException(e); - } catch (final Exception e) { - throw ArangoDBException.wrap(e); + if (timeout == null || timeout == 0L) { + return future; + } else { + return CompletableFutureUtils.orTimeout(future, timeout, TimeUnit.MILLISECONDS); } } @Override protected void doKeepAlive() { - sendKeepAlive(); + sendKeepAlive().join(); } } diff --git a/vst/src/main/java/com/arangodb/vst/internal/utils/CompletableFutureUtils.java b/vst/src/main/java/com/arangodb/vst/internal/utils/CompletableFutureUtils.java new file mode 100644 index 000000000..2321ac5aa --- /dev/null +++ b/vst/src/main/java/com/arangodb/vst/internal/utils/CompletableFutureUtils.java @@ -0,0 +1,24 @@ +package com.arangodb.vst.internal.utils; + +import java.util.concurrent.*; + +public class CompletableFutureUtils { + + private CompletableFutureUtils() { + } + + private static final ScheduledExecutorService timeoutScheduler = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = Executors.defaultThreadFactory().newThread(r); + t.setDaemon(true); + return t; + } + ); + + public static CompletableFuture orTimeout(CompletableFuture completableFuture, long timeout, TimeUnit unit) { + ScheduledFuture timeoutTask = timeoutScheduler.schedule(() -> + completableFuture.completeExceptionally(new TimeoutException()), timeout, unit); + completableFuture.whenComplete((v, e) -> timeoutTask.cancel(false)); + return completableFuture; + } + +} From c507d79fcded570340d481a392c234246c3454a9 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 10 Oct 2023 22:56:06 +0200 Subject: [PATCH 12/62] internal generics refactoring --- .../internal/ArangoCollectionImpl.java | 3 +- .../com/arangodb/internal/ArangoDBImpl.java | 2 +- .../arangodb/internal/ArangoDatabaseImpl.java | 3 +- .../internal/ArangoEdgeCollectionImpl.java | 4 +- ...teable.java => ArangoExecuteableSync.java} | 8 +-- .../com/arangodb/internal/ArangoExecutor.java | 20 +++++- .../internal/ArangoExecutorAsync.java | 69 +++++++++++++++++++ .../arangodb/internal/ArangoExecutorSync.java | 19 +---- .../arangodb/internal/ArangoGraphImpl.java | 3 +- .../arangodb/internal/ArangoSearchImpl.java | 3 +- .../internal/ArangoVertexCollectionImpl.java | 4 +- .../com/arangodb/internal/ArangoViewImpl.java | 3 +- .../internal/InternalArangoCollection.java | 11 ++- .../arangodb/internal/InternalArangoDB.java | 4 +- .../internal/InternalArangoDatabase.java | 9 +-- .../InternalArangoEdgeCollection.java | 11 ++- .../internal/InternalArangoGraph.java | 11 ++- .../internal/InternalArangoSearch.java | 6 +- .../InternalArangoVertexCollection.java | 11 ++- .../arangodb/internal/InternalArangoView.java | 11 ++- .../internal/InternalSearchAlias.java | 5 +- .../arangodb/internal/SearchAliasImpl.java | 3 +- .../internal/cursor/ArangoCursorImpl.java | 6 +- 23 files changed, 140 insertions(+), 89 deletions(-) rename core/src/main/java/com/arangodb/internal/{ArangoExecuteable.java => ArangoExecuteableSync.java} (87%) create mode 100644 core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java index b16394e9b..7eb0e9767 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java @@ -37,8 +37,7 @@ * @author Mark Vollmary * @author Michele Rastelli */ -public class ArangoCollectionImpl extends InternalArangoCollection - implements ArangoCollection { +public class ArangoCollectionImpl extends InternalArangoCollection implements ArangoCollection { private static final Logger LOGGER = LoggerFactory.getLogger(ArangoCollectionImpl.class); diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java index 09bb02365..711a4dbfd 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java @@ -38,7 +38,7 @@ * @author Heiko Kernbach * @author Michele Rastelli */ -public class ArangoDBImpl extends InternalArangoDB implements ArangoDB { +public class ArangoDBImpl extends InternalArangoDB implements ArangoDB { private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBImpl.class); private final HostHandler hostHandler; diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java index cce57fa93..065ad4f67 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java @@ -41,8 +41,7 @@ * @author Mark Vollmary * @author Michele Rastelli */ -public class ArangoDatabaseImpl extends InternalArangoDatabase - implements ArangoDatabase { +public class ArangoDatabaseImpl extends InternalArangoDatabase implements ArangoDatabase { protected ArangoDatabaseImpl(final ArangoDBImpl arangoDB, final String name) { super(arangoDB, name); diff --git a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java index 2af8005ae..b0fdea6b8 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java @@ -31,9 +31,7 @@ /** * @author Mark Vollmary */ -public class ArangoEdgeCollectionImpl - extends InternalArangoEdgeCollection - implements ArangoEdgeCollection { +public class ArangoEdgeCollectionImpl extends InternalArangoEdgeCollection implements ArangoEdgeCollection { private static final Logger LOGGER = LoggerFactory.getLogger(ArangoEdgeCollectionImpl.class); diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java b/core/src/main/java/com/arangodb/internal/ArangoExecuteableSync.java similarity index 87% rename from core/src/main/java/com/arangodb/internal/ArangoExecuteable.java rename to core/src/main/java/com/arangodb/internal/ArangoExecuteableSync.java index 1dc33aade..5fb9e3e2d 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecuteableSync.java @@ -27,14 +27,14 @@ /** * @author Mark Vollmary */ -public abstract class ArangoExecuteable implements ArangoSerdeAccessor { +public abstract class ArangoExecuteableSync implements ArangoSerdeAccessor { private static final String SLASH = "/"; - protected final E executor; + protected final ArangoExecutorSync executor; protected final InternalSerde serde; - protected ArangoExecuteable(final E executor, final InternalSerde serde) { + protected ArangoExecuteableSync(final ArangoExecutorSync executor, final InternalSerde serde) { super(); this.executor = executor; this.serde = serde; @@ -57,7 +57,7 @@ protected static String createPath(final String... params) { return sb.toString(); } - protected E executor() { + protected ArangoExecutorSync executor() { return executor; } diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecutor.java b/core/src/main/java/com/arangodb/internal/ArangoExecutor.java index a37f4b11c..da072eb98 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecutor.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecutor.java @@ -20,10 +20,13 @@ package com.arangodb.internal; +import com.arangodb.ArangoDBException; import com.arangodb.QueueTimeMetrics; import com.arangodb.internal.config.ArangoConfig; +import com.arangodb.internal.net.CommunicationProtocol; import com.arangodb.internal.serde.InternalSerde; +import java.io.IOException; import java.lang.reflect.Type; /** @@ -31,17 +34,30 @@ */ public abstract class ArangoExecutor { + protected final CommunicationProtocol protocol; private final QueueTimeMetricsImpl qtMetrics; private final InternalSerde serde; private final String timeoutS; - protected ArangoExecutor(final ArangoConfig config) { - super(); + protected ArangoExecutor(final CommunicationProtocol protocol, final ArangoConfig config ) { + this.protocol = protocol; qtMetrics = new QueueTimeMetricsImpl(config.getResponseQueueTimeSamples()); serde = config.getInternalSerde(); timeoutS = config.getTimeout() >= 1000 ? Integer.toString(config.getTimeout() / 1000) : null; } + public void disconnect() { + try { + protocol.close(); + } catch (final IOException e) { + throw new ArangoDBException(e); + } + } + + public void setJwt(String jwt) { + protocol.setJwt(jwt); + } + protected T createResult(final Type type, final InternalResponse response) { return serde.deserialize(response.getBody(), type); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java b/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java new file mode 100644 index 000000000..0aabc9ac3 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java @@ -0,0 +1,69 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.ArangoDBException; +import com.arangodb.internal.config.ArangoConfig; +import com.arangodb.internal.net.CommunicationProtocol; +import com.arangodb.internal.net.HostHandle; + +import java.lang.reflect.Type; +import java.util.concurrent.CompletableFuture; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +public class ArangoExecutorAsync extends ArangoExecutor { + + public ArangoExecutorAsync(final CommunicationProtocol protocol, final ArangoConfig config) { + super(protocol, config); + } + + public CompletableFuture execute(final InternalRequest request, final Type type) { + return execute(request, type, null); + } + + public CompletableFuture execute(final InternalRequest request, final Type type, final HostHandle hostHandle) { + return execute(request, response -> createResult(type, response), hostHandle); + } + + public CompletableFuture execute(final InternalRequest request, final ResponseDeserializer responseDeserializer) { + return execute(request, responseDeserializer, null); + } + + public CompletableFuture execute( + final InternalRequest request, + final ResponseDeserializer responseDeserializer, + final HostHandle hostHandle) { + + return protocol.executeAsync(interceptRequest(request), hostHandle) + .handle((r, e) -> { + if (e != null) { + throw ArangoDBException.wrap(e); + } else { + interceptResponse(r); + return responseDeserializer.deserialize(r); + } + }); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecutorSync.java b/core/src/main/java/com/arangodb/internal/ArangoExecutorSync.java index c0f10815f..9da32958e 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecutorSync.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecutorSync.java @@ -20,12 +20,10 @@ package com.arangodb.internal; -import com.arangodb.ArangoDBException; import com.arangodb.internal.config.ArangoConfig; import com.arangodb.internal.net.CommunicationProtocol; import com.arangodb.internal.net.HostHandle; -import java.io.IOException; import java.lang.reflect.Type; /** @@ -33,11 +31,8 @@ */ public class ArangoExecutorSync extends ArangoExecutor { - private final CommunicationProtocol protocol; - public ArangoExecutorSync(final CommunicationProtocol protocol, final ArangoConfig config) { - super(config); - this.protocol = protocol; + super(protocol, config); } public T execute(final InternalRequest request, final Type type) { @@ -62,16 +57,4 @@ public T execute( return responseDeserializer.deserialize(response); } - public void disconnect() { - try { - protocol.close(); - } catch (final IOException e) { - throw new ArangoDBException(e); - } - } - - public void setJwt(String jwt) { - protocol.setJwt(jwt); - } - } diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java index 17c599d8a..d70853e8b 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java @@ -35,8 +35,7 @@ /** * @author Mark Vollmary */ -public class ArangoGraphImpl extends InternalArangoGraph - implements ArangoGraph { +public class ArangoGraphImpl extends InternalArangoGraph implements ArangoGraph { protected ArangoGraphImpl(final ArangoDatabaseImpl db, final String name) { super(db, name); diff --git a/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java b/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java index 1d914b207..7d66ff087 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java @@ -30,8 +30,7 @@ /** * @author Mark Vollmary */ -public class ArangoSearchImpl extends InternalArangoSearch - implements ArangoSearch { +public class ArangoSearchImpl extends InternalArangoSearch implements ArangoSearch { protected ArangoSearchImpl(final ArangoDatabaseImpl db, final String name) { super(db, name); diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java index 1943b5659..7bcdb6a34 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java @@ -31,9 +31,7 @@ /** * @author Mark Vollmary */ -public class ArangoVertexCollectionImpl - extends InternalArangoVertexCollection - implements ArangoVertexCollection { +public class ArangoVertexCollectionImpl extends InternalArangoVertexCollection implements ArangoVertexCollection { private static final Logger LOGGER = LoggerFactory.getLogger(ArangoVertexCollectionImpl.class); diff --git a/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java b/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java index 06f655b73..8a3df3b56 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java @@ -27,8 +27,7 @@ /** * @author Mark Vollmary */ -public class ArangoViewImpl extends InternalArangoView - implements ArangoView { +public class ArangoViewImpl extends InternalArangoView implements ArangoView { protected ArangoViewImpl(final ArangoDatabaseImpl db, final String name) { super(db, name); diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java index b25cb5f2a..340070ba2 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java @@ -21,6 +21,7 @@ package com.arangodb.internal; import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabase; import com.arangodb.entity.*; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; import com.arangodb.internal.util.DocumentUtil; @@ -39,9 +40,7 @@ * @author Mark Vollmary * @author Michele Rastelli */ -public abstract class InternalArangoCollection, D extends InternalArangoDatabase, - E extends ArangoExecutor> - extends ArangoExecuteable { +public abstract class InternalArangoCollection extends ArangoExecuteableSync { protected static final String PATH_API_COLLECTION = "/_api/collection"; private static final String COLLECTION = "collection"; @@ -61,16 +60,16 @@ public abstract class InternalArangoCollection, D private static final String TRANSACTION_ID = "x-arango-trx-id"; - private final D db; + private final ArangoDatabase db; protected final String name; - protected InternalArangoCollection(final D db, final String name) { + protected InternalArangoCollection(final ArangoDatabaseImpl db, final String name) { super(db.executor, db.serde); this.db = db; this.name = name; } - public D db() { + public ArangoDatabase db() { return db; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java index 595b7c601..dd537c925 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java @@ -40,7 +40,7 @@ * @author Mark Vollmary * @author Heiko Kernbach */ -public abstract class InternalArangoDB extends ArangoExecuteable { +public abstract class InternalArangoDB extends ArangoExecuteableSync { private static final String PATH_API_ADMIN_LOG_ENTRIES = "/_admin/log/entries"; private static final String PATH_API_ADMIN_LOG_LEVEL = "/_admin/log/level"; private static final String PATH_API_ROLE = "/_admin/server/role"; @@ -48,7 +48,7 @@ public abstract class InternalArangoDB extends ArangoE private static final String PATH_API_USER = "/_api/user"; private static final String PATH_API_QUERY_RULES = "/_api/query/rules"; - protected InternalArangoDB(final E executor, final InternalSerde util) { + protected InternalArangoDB(final ArangoExecutorSync executor, final InternalSerde util) { super(executor, util); } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java index 5745239ea..2212ff55f 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java @@ -20,6 +20,7 @@ package com.arangodb.internal; +import com.arangodb.ArangoDB; import com.arangodb.entity.*; import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; @@ -37,7 +38,7 @@ * @author Mark Vollmary * @author Michele Rastelli */ -public abstract class InternalArangoDatabase, EXECUTOR extends ArangoExecutor> extends ArangoExecuteable { +public abstract class InternalArangoDatabase extends ArangoExecuteableSync { protected static final String PATH_API_DATABASE = "/_api/database"; private static final String PATH_API_VERSION = "/_api/version"; @@ -59,15 +60,15 @@ public abstract class InternalArangoDatabase, D extends InternalArangoDatabase, G extends InternalArangoGraph, E extends ArangoExecutor> - extends ArangoExecuteable { +public abstract class InternalArangoEdgeCollection extends ArangoExecuteableSync { private static final String PATH_API_GHARIAL = "/_api/gharial"; private static final String TRANSACTION_ID = "x-arango-trx-id"; private static final String EDGE_PATH = "edge"; private static final String EDGE_JSON_POINTER = "/edge"; - private final G graph; + private final ArangoGraphImpl graph; private final String name; - protected InternalArangoEdgeCollection(final G graph, final String name) { + protected InternalArangoEdgeCollection(final ArangoGraphImpl graph, final String name) { super(graph.executor, graph.serde); this.graph = graph; this.name = name; } - public G graph() { + public ArangoGraph graph() { return graph; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java b/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java index bff2aef0e..4fd2bd601 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java @@ -20,6 +20,7 @@ package com.arangodb.internal; +import com.arangodb.ArangoDatabase; import com.arangodb.entity.EdgeDefinition; import com.arangodb.entity.GraphEntity; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; @@ -34,25 +35,23 @@ /** * @author Mark Vollmary */ -public abstract class InternalArangoGraph, D extends InternalArangoDatabase, - E extends ArangoExecutor> - extends ArangoExecuteable { +public abstract class InternalArangoGraph extends ArangoExecuteableSync { protected static final String PATH_API_GHARIAL = "/_api/gharial"; private static final String GRAPH = "/graph"; private static final String VERTEX = "vertex"; private static final String EDGE = "edge"; - private final D db; + private final ArangoDatabaseImpl db; private final String name; - protected InternalArangoGraph(final D db, final String name) { + protected InternalArangoGraph(final ArangoDatabaseImpl db, final String name) { super(db.executor, db.serde); this.db = db; this.name = name; } - public D db() { + public ArangoDatabase db() { return db; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java b/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java index 78846dd42..90b73c445 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java @@ -25,13 +25,11 @@ /** * @author Mark Vollmary */ -public class InternalArangoSearch, D extends InternalArangoDatabase, - E extends ArangoExecutor> - extends InternalArangoView { +public class InternalArangoSearch extends InternalArangoView { private static final String PROPERTIES_PATH = "properties"; - protected InternalArangoSearch(final D db, final String name) { + protected InternalArangoSearch(final ArangoDatabaseImpl db, final String name) { super(db, name); } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java index 1eab27ff8..37a63f3c1 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java @@ -20,6 +20,7 @@ package com.arangodb.internal; +import com.arangodb.ArangoGraph; import com.arangodb.entity.VertexEntity; import com.arangodb.entity.VertexUpdateEntity; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; @@ -30,25 +31,23 @@ /** * @author Mark Vollmary */ -public abstract class InternalArangoVertexCollection, - D extends InternalArangoDatabase, G extends InternalArangoGraph, E extends ArangoExecutor> - extends ArangoExecuteable { +public abstract class InternalArangoVertexCollection extends ArangoExecuteableSync { private static final String PATH_API_GHARIAL = "/_api/gharial"; private static final String VERTEX_PATH = "vertex"; private static final String VERTEX_JSON_POINTER = "/vertex"; private static final String TRANSACTION_ID = "x-arango-trx-id"; - private final G graph; + private final ArangoGraphImpl graph; private final String name; - protected InternalArangoVertexCollection(final G graph, final String name) { + protected InternalArangoVertexCollection(final ArangoGraphImpl graph, final String name) { super(graph.executor, graph.serde); this.graph = graph; this.name = name; } - public G graph() { + public ArangoGraph graph() { return graph; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoView.java b/core/src/main/java/com/arangodb/internal/InternalArangoView.java index 8016c54ef..2476f83dc 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoView.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoView.java @@ -20,6 +20,7 @@ package com.arangodb.internal; +import com.arangodb.ArangoDatabase; import com.arangodb.model.OptionsBuilder; import com.arangodb.model.ViewRenameOptions; @@ -27,23 +28,21 @@ * @author Mark Vollmary * @author Michele Rastelli */ -public abstract class InternalArangoView, D extends InternalArangoDatabase, - E extends ArangoExecutor> - extends ArangoExecuteable { +public abstract class InternalArangoView extends ArangoExecuteableSync { protected static final String PATH_API_VIEW = "/_api/view"; protected static final String PATH_API_ANALYZER = "/_api/analyzer"; - protected final D db; + protected final ArangoDatabaseImpl db; protected final String name; - protected InternalArangoView(final D db, final String name) { + protected InternalArangoView(final ArangoDatabaseImpl db, final String name) { super(db.executor, db.serde); this.db = db; this.name = name; } - public D db() { + public ArangoDatabase db() { return db; } diff --git a/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java b/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java index e8a2fe697..ae8aecad8 100644 --- a/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java +++ b/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java @@ -22,12 +22,11 @@ import com.arangodb.model.arangosearch.SearchAliasPropertiesOptions; -public class InternalSearchAlias, D extends InternalArangoDatabase, E extends ArangoExecutor> - extends InternalArangoView { +public class InternalSearchAlias extends InternalArangoView { private static final String PROPERTIES_PATH = "properties"; - protected InternalSearchAlias(final D db, final String name) { + protected InternalSearchAlias(final ArangoDatabaseImpl db, final String name) { super(db, name); } diff --git a/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java b/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java index 3734491ba..10fd33b25 100644 --- a/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java +++ b/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java @@ -30,8 +30,7 @@ /** * @author Michele Rastelli */ -public class SearchAliasImpl extends InternalSearchAlias - implements SearchAlias { +public class SearchAliasImpl extends InternalSearchAlias implements SearchAlias { protected SearchAliasImpl(final ArangoDatabaseImpl db, final String name) { super(db, name); diff --git a/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorImpl.java b/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorImpl.java index b17bc6e06..0964d43f0 100644 --- a/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorImpl.java +++ b/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorImpl.java @@ -45,7 +45,7 @@ public class ArangoCursorImpl implements ArangoCursor { private final boolean pontentialDirtyRead; private final boolean allowRetry; - public ArangoCursorImpl(final InternalArangoDatabase db, final ArangoCursorExecute execute, + public ArangoCursorImpl(final InternalArangoDatabase db, final ArangoCursorExecute execute, final Class type, final InternalCursorEntity result) { super(); this.execute = execute; @@ -128,13 +128,13 @@ protected ArangoCursorExecute getExecute() { protected static class ArangoCursorIterator implements ArangoIterator { private final String cursorId; private final Class type; - private final InternalArangoDatabase db; + private final InternalArangoDatabase db; private final ArangoCursorExecute execute; private InternalCursorEntity result; private Iterator arrayIterator; protected ArangoCursorIterator(final String cursorId, final Class type, final ArangoCursorExecute execute, - final InternalArangoDatabase db, final InternalCursorEntity result) { + final InternalArangoDatabase db, final InternalCursorEntity result) { this.cursorId = cursorId; this.type = type; this.execute = execute; From 73cb1af5246d989fb082e02e00ea3eb3ffdc525d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 11 Oct 2023 13:09:52 +0200 Subject: [PATCH 13/62] ArangoExecuteable refactoring --- .../internal/ArangoCollectionAsyncImpl.java | 502 ++++++++++++++++++ .../internal/ArangoCollectionImpl.java | 100 ++-- .../arangodb/internal/ArangoDBAsyncImpl.java | 208 ++++++++ .../com/arangodb/internal/ArangoDBImpl.java | 48 +- .../arangodb/internal/ArangoDatabaseImpl.java | 102 ++-- .../internal/ArangoEdgeCollectionImpl.java | 22 +- ...teableSync.java => ArangoExecuteable.java} | 36 +- .../arangodb/internal/ArangoGraphImpl.java | 16 +- .../arangodb/internal/ArangoSearchImpl.java | 12 +- .../internal/ArangoVertexCollectionImpl.java | 22 +- .../com/arangodb/internal/ArangoViewImpl.java | 6 +- .../internal/InternalArangoCollection.java | 4 +- .../arangodb/internal/InternalArangoDB.java | 12 +- .../internal/InternalArangoDatabase.java | 4 +- .../InternalArangoEdgeCollection.java | 4 +- .../internal/InternalArangoGraph.java | 4 +- .../InternalArangoVertexCollection.java | 4 +- .../arangodb/internal/InternalArangoView.java | 4 +- .../arangodb/internal/SearchAliasImpl.java | 12 +- 19 files changed, 927 insertions(+), 195 deletions(-) create mode 100644 core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java create mode 100644 core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java rename core/src/main/java/com/arangodb/internal/{ArangoExecuteableSync.java => ArangoExecuteable.java} (59%) diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java new file mode 100644 index 000000000..8c0c081fa --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java @@ -0,0 +1,502 @@ +///* +// * DISCLAIMER +// * +// * Copyright 2018 ArangoDB GmbH, Cologne, Germany +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// * +// * Copyright holder is ArangoDB GmbH, Cologne, Germany +// */ +// +//package com.arangodb.internal; +// +//import com.arangodb.ArangoCollection; +//import com.arangodb.ArangoCollectionAsync; +//import com.arangodb.ArangoDBException; +//import com.arangodb.entity.*; +//import com.arangodb.internal.util.DocumentUtil; +//import com.arangodb.model.*; +//import com.arangodb.util.RawData; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +// +//import java.util.Collection; +// +//import static com.arangodb.internal.serde.SerdeUtils.constructParametricType; +// +///** +// * @author Mark Vollmary +// * @author Michele Rastelli +// */ +//public class ArangoCollectionAsyncImpl extends InternalArangoCollection implements ArangoCollectionAsync { +// +// private static final Logger LOGGER = LoggerFactory.getLogger(ArangoCollectionAsyncImpl.class); +// +// protected ArangoCollectionAsyncImpl(final ArangoDatabaseImpl db, final String name) { +// super(db, name); +// } +// +// @Override +// public DocumentCreateEntity insertDocument(final Object value) { +// return executor.execute(insertDocumentRequest(value, new DocumentCreateOptions()), +// constructParametricType(DocumentCreateEntity.class, Void.class)); +// } +// +// @Override +// @SuppressWarnings("unchecked") +// public DocumentCreateEntity insertDocument(final T value, final DocumentCreateOptions options) { +// return insertDocument(value, options, (Class) value.getClass()); +// } +// +// @Override +// public DocumentCreateEntity insertDocument(final T value, final DocumentCreateOptions options, +// final Class type) { +// return executor.execute(insertDocumentRequest(value, options), +// constructParametricType(DocumentCreateEntity.class, type)); +// } +// +// @Override +// public MultiDocumentEntity> insertDocuments(RawData values) { +// return executor +// .execute(insertDocumentsRequest(values, new DocumentCreateOptions()), +// insertDocumentsResponseDeserializer(Void.class)); +// } +// +// @Override +// @SuppressWarnings("unchecked") +// public MultiDocumentEntity> insertDocuments(RawData values, +// DocumentCreateOptions options) { +// return executor +// .execute(insertDocumentsRequest(values, options), +// insertDocumentsResponseDeserializer((Class) values.getClass())); +// } +// +// @Override +// public MultiDocumentEntity> insertDocuments(final Iterable values) { +// return insertDocuments(values, new DocumentCreateOptions()); +// } +// +// @Override +// public MultiDocumentEntity> insertDocuments( +// final Iterable values, final DocumentCreateOptions options) { +// return executor +// .execute(insertDocumentsRequest(values, options), +// insertDocumentsResponseDeserializer(Void.class)); +// } +// +// @Override +// public MultiDocumentEntity> insertDocuments(Iterable values, +// DocumentCreateOptions options, +// Class type) { +// return executor +// .execute(insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer(type)); +// } +// +// @Override +// public DocumentImportEntity importDocuments(final Iterable values) { +// return importDocuments(values, new DocumentImportOptions()); +// } +// +// @Override +// public DocumentImportEntity importDocuments(final Iterable values, final DocumentImportOptions options) { +// return executor.execute(importDocumentsRequest(values, options), DocumentImportEntity.class); +// } +// +// @Override +// public DocumentImportEntity importDocuments(RawData values) { +// return importDocuments(values, new DocumentImportOptions()); +// } +// +// @Override +// public DocumentImportEntity importDocuments(RawData values, DocumentImportOptions options) { +// return executor.execute(importDocumentsRequest(values, options), DocumentImportEntity.class); +// } +// +// @Override +// public T getDocument(final String key, final Class type) { +// return getDocument(key, type, new DocumentReadOptions()); +// } +// +// @Override +// public T getDocument(final String key, final Class type, final DocumentReadOptions options) { +// DocumentUtil.validateDocumentKey(key); +// try { +// return executor.execute(getDocumentRequest(key, options), getDocumentResponseDeserializer(type)); +// } catch (final ArangoDBException e) { +// if (LOGGER.isDebugEnabled()) { +// LOGGER.debug(e.getMessage(), e); +// } +// +// // handle Response: 404, Error: 1655 - transaction not found +// if (e.getErrorNum() != null && e.getErrorNum() == 1655) { +// throw e; +// } +// +// if ((e.getResponseCode() != null && (e.getResponseCode() == 404 || e.getResponseCode() == 304 +// || e.getResponseCode() == 412))) { +// return null; +// } +// throw e; +// } +// } +// +// @Override +// public MultiDocumentEntity getDocuments(final Iterable keys, final Class type) { +// return getDocuments(keys, type, new DocumentReadOptions()); +// } +// +// @Override +// public MultiDocumentEntity getDocuments( +// final Iterable keys, final Class type, final DocumentReadOptions options) { +// return executor.execute(getDocumentsRequest(keys, options), getDocumentsResponseDeserializer(type)); +// } +// +// @Override +// public DocumentUpdateEntity replaceDocument(final String key, final Object value) { +// return executor.execute(replaceDocumentRequest(key, value, new DocumentReplaceOptions()), +// constructParametricType(DocumentUpdateEntity.class, Void.class)); +// } +// +// @Override +// @SuppressWarnings("unchecked") +// public DocumentUpdateEntity replaceDocument( +// final String key, final T value, final DocumentReplaceOptions options) { +// return replaceDocument(key, value, options, (Class) value.getClass()); +// } +// +// @Override +// public DocumentUpdateEntity replaceDocument(String key, T value, DocumentReplaceOptions options, +// Class type) { +// return executor.execute(replaceDocumentRequest(key, value, options), +// constructParametricType(DocumentUpdateEntity.class, type)); +// } +// +// @Override +// public MultiDocumentEntity> replaceDocuments(RawData values) { +// return executor.execute(replaceDocumentsRequest(values, new DocumentReplaceOptions()), +// replaceDocumentsResponseDeserializer(Void.class)); +// } +// +// @Override +// @SuppressWarnings("unchecked") +// public MultiDocumentEntity> replaceDocuments(RawData values, +// DocumentReplaceOptions options) { +// return executor.execute(replaceDocumentsRequest(values, options), +// replaceDocumentsResponseDeserializer((Class) values.getClass())); +// } +// +// @Override +// public MultiDocumentEntity> replaceDocuments(final Iterable values) { +// return replaceDocuments(values, new DocumentReplaceOptions()); +// } +// +// @Override +// public MultiDocumentEntity> replaceDocuments( +// final Iterable values, final DocumentReplaceOptions options) { +// return executor.execute(replaceDocumentsRequest(values, options), +// replaceDocumentsResponseDeserializer(Void.class)); +// } +// +// @Override +// public MultiDocumentEntity> replaceDocuments(Iterable values, +// DocumentReplaceOptions options, +// Class type) { +// return executor.execute(replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(type)); +// } +// +// @Override +// public DocumentUpdateEntity updateDocument(final String key, final Object value) { +// return updateDocument(key, value, new DocumentUpdateOptions(), Void.class); +// } +// +// @Override +// @SuppressWarnings("unchecked") +// public DocumentUpdateEntity updateDocument( +// final String key, final T value, final DocumentUpdateOptions options) { +// return updateDocument(key, value, options, (Class) value.getClass()); +// } +// +// @Override +// public DocumentUpdateEntity updateDocument( +// final String key, final Object value, final DocumentUpdateOptions options, final Class returnType) { +// return executor.execute(updateDocumentRequest(key, value, options), +// constructParametricType(DocumentUpdateEntity.class, returnType)); +// } +// +// @Override +// public MultiDocumentEntity> updateDocuments(RawData values) { +// return executor +// .execute(updateDocumentsRequest(values, new DocumentUpdateOptions()), +// updateDocumentsResponseDeserializer(Void.class)); +// } +// +// @Override +// @SuppressWarnings("unchecked") +// public MultiDocumentEntity> updateDocuments(RawData values, +// DocumentUpdateOptions options) { +// return executor +// .execute(updateDocumentsRequest(values, options), +// updateDocumentsResponseDeserializer((Class) values.getClass())); +// } +// +// @Override +// public MultiDocumentEntity> updateDocuments(final Iterable values) { +// return updateDocuments(values, new DocumentUpdateOptions()); +// } +// +// @Override +// public MultiDocumentEntity> updateDocuments( +// final Iterable values, final DocumentUpdateOptions options) { +// return updateDocuments(values, options, Void.class); +// } +// +// @Override +// public MultiDocumentEntity> updateDocuments( +// final Iterable values, final DocumentUpdateOptions options, final Class returnType) { +// return executor +// .execute(updateDocumentsRequest(values, options), updateDocumentsResponseDeserializer(returnType)); +// } +// +// @Override +// public DocumentDeleteEntity deleteDocument(final String key) { +// return deleteDocument(key, new DocumentDeleteOptions()); +// } +// +// @Override +// public DocumentDeleteEntity deleteDocument(String key, DocumentDeleteOptions options) { +// return deleteDocument(key, options, Void.class); +// } +// +// @Override +// public DocumentDeleteEntity deleteDocument( +// final String key, final DocumentDeleteOptions options, final Class type) { +// return executor.execute(deleteDocumentRequest(key, options), +// constructParametricType(DocumentDeleteEntity.class, type)); +// } +// +// @Override +// public MultiDocumentEntity> deleteDocuments(RawData values) { +// return executor.execute(deleteDocumentsRequest(values, new DocumentDeleteOptions()), +// deleteDocumentsResponseDeserializer(Void.class)); +// } +// +// @Override +// @SuppressWarnings("unchecked") +// public MultiDocumentEntity> deleteDocuments(RawData values, +// DocumentDeleteOptions options) { +// return executor.execute(deleteDocumentsRequest(values, options), +// deleteDocumentsResponseDeserializer((Class) values.getClass())); +// } +// +// @Override +// public MultiDocumentEntity> deleteDocuments(final Iterable values) { +// return deleteDocuments(values, new DocumentDeleteOptions()); +// } +// +// @Override +// public MultiDocumentEntity> deleteDocuments( +// final Iterable values, final DocumentDeleteOptions options) { +// return deleteDocuments(values, options, Void.class); +// } +// +// @Override +// public MultiDocumentEntity> deleteDocuments( +// final Iterable values, final DocumentDeleteOptions options, final Class type) { +// return executor.execute(deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer(type)); +// } +// +// @Override +// public Boolean documentExists(final String key) { +// return documentExists(key, new DocumentExistsOptions()); +// } +// +// @Override +// public Boolean documentExists(final String key, final DocumentExistsOptions options) { +// try { +// executor.execute(documentExistsRequest(key, options), Void.class); +// return true; +// } catch (final ArangoDBException e) { +// +// // handle Response: 404, Error: 1655 - transaction not found +// if (e.getErrorNum() != null && e.getErrorNum() == 1655) { +// throw e; +// } +// +// if ((e.getResponseCode() != null && +// (e.getResponseCode() == 404 || e.getResponseCode() == 304 || e.getResponseCode() == 412))) { +// return false; +// } +// throw e; +// } +// } +// +// @Override +// public IndexEntity getIndex(final String id) { +// return executor.execute(getIndexRequest(id), IndexEntity.class); +// } +// +// @Override +// public InvertedIndexEntity getInvertedIndex(String id) { +// return executor.execute(getIndexRequest(id), InvertedIndexEntity.class); +// } +// +// @Override +// public String deleteIndex(final String id) { +// return executor.execute(deleteIndexRequest(id), deleteIndexResponseDeserializer()); +// } +// +// @Override +// public IndexEntity ensurePersistentIndex(final Iterable fields, final PersistentIndexOptions options) { +// return executor.execute(createPersistentIndexRequest(fields, options), IndexEntity.class); +// } +// +// @Override +// public InvertedIndexEntity ensureInvertedIndex(final InvertedIndexOptions options) { +// return executor.execute(createInvertedIndexRequest(options), InvertedIndexEntity.class); +// } +// +// @Override +// public IndexEntity ensureGeoIndex(final Iterable fields, final GeoIndexOptions options) { +// return executor.execute(createGeoIndexRequest(fields, options), IndexEntity.class); +// } +// +// @Deprecated +// @Override +// public IndexEntity ensureFulltextIndex(final Iterable fields, final FulltextIndexOptions options) { +// return executor.execute(createFulltextIndexRequest(fields, options), IndexEntity.class); +// } +// +// @Override +// public IndexEntity ensureTtlIndex(final Iterable fields, final TtlIndexOptions options) { +// return executor.execute(createTtlIndexRequest(fields, options), IndexEntity.class); +// } +// +// @Override +// public IndexEntity ensureZKDIndex(final Iterable fields, final ZKDIndexOptions options) { +// return executor.execute(createZKDIndexRequest(fields, options), IndexEntity.class); +// } +// +// @Override +// public Collection getIndexes() { +// return executor.execute(getIndexesRequest(), getIndexesResponseDeserializer()); +// } +// +// @Override +// public Collection getInvertedIndexes() { +// return executor.execute(getIndexesRequest(), getInvertedIndexesResponseDeserializer()); +// } +// +// @Override +// public boolean exists() { +// try { +// getInfo(); +// return true; +// } catch (final ArangoDBException e) { +// if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(e.getErrorNum())) { +// return false; +// } +// throw e; +// } +// } +// +// @Override +// public CollectionEntity truncate() { +// return truncate(null); +// } +// +// @Override +// public CollectionEntity truncate(CollectionTruncateOptions options) { +// return executor.execute(truncateRequest(options), CollectionEntity.class); +// } +// +// @Override +// public CollectionPropertiesEntity count() { +// return count(null); +// } +// +// @Override +// public CollectionPropertiesEntity count(CollectionCountOptions options) { +// return executor.execute(countRequest(options), CollectionPropertiesEntity.class); +// } +// +// @Override +// public CollectionEntity create() { +// return db().createCollection(name()); +// } +// +// @Override +// public CollectionEntity create(final CollectionCreateOptions options) { +// return db().createCollection(name(), options); +// } +// +// @Override +// public void drop() { +// executor.execute(dropRequest(null), Void.class); +// } +// +// @Override +// public void drop(final boolean isSystem) { +// executor.execute(dropRequest(isSystem), Void.class); +// } +// +// @Override +// public CollectionEntity getInfo() { +// return executor.execute(getInfoRequest(), CollectionEntity.class); +// } +// +// @Override +// public CollectionPropertiesEntity getProperties() { +// return executor.execute(getPropertiesRequest(), CollectionPropertiesEntity.class); +// } +// +// @Override +// public CollectionPropertiesEntity changeProperties(final CollectionPropertiesOptions options) { +// return executor.execute(changePropertiesRequest(options), CollectionPropertiesEntity.class); +// } +// +// @Override +// public CollectionEntity rename(final String newName) { +// return executor.execute(renameRequest(newName), CollectionEntity.class); +// } +// +// @Override +// public ShardEntity getResponsibleShard(final Object value) { +// return executor.execute(responsibleShardRequest(value), ShardEntity.class); +// } +// +// @Override +// public CollectionRevisionEntity getRevision() { +// return executor.execute(getRevisionRequest(), CollectionRevisionEntity.class); +// } +// +// @Override +// public void grantAccess(final String user, final Permissions permissions) { +// executor.execute(grantAccessRequest(user, permissions), Void.class); +// } +// +// @Override +// public void revokeAccess(final String user) { +// executor.execute(grantAccessRequest(user, Permissions.NONE), Void.class); +// } +// +// @Override +// public void resetAccess(final String user) { +// executor.execute(resetAccessRequest(user), Void.class); +// } +// +// @Override +// public Permissions getPermissions(final String user) { +// return executor.execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); +// } +// +//} diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java index 7eb0e9767..a68f4ad81 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java @@ -47,7 +47,7 @@ protected ArangoCollectionImpl(final ArangoDatabaseImpl db, final String name) { @Override public DocumentCreateEntity insertDocument(final Object value) { - return executor.execute(insertDocumentRequest(value, new DocumentCreateOptions()), + return executorSync().execute(insertDocumentRequest(value, new DocumentCreateOptions()), constructParametricType(DocumentCreateEntity.class, Void.class)); } @@ -60,13 +60,13 @@ public DocumentCreateEntity insertDocument(final T value, final DocumentC @Override public DocumentCreateEntity insertDocument(final T value, final DocumentCreateOptions options, final Class type) { - return executor.execute(insertDocumentRequest(value, options), + return executorSync().execute(insertDocumentRequest(value, options), constructParametricType(DocumentCreateEntity.class, type)); } @Override public MultiDocumentEntity> insertDocuments(RawData values) { - return executor + return executorSync() .execute(insertDocumentsRequest(values, new DocumentCreateOptions()), insertDocumentsResponseDeserializer(Void.class)); } @@ -75,7 +75,7 @@ public MultiDocumentEntity> insertDocuments(RawData v @SuppressWarnings("unchecked") public MultiDocumentEntity> insertDocuments(RawData values, DocumentCreateOptions options) { - return executor + return executorSync() .execute(insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer((Class) values.getClass())); } @@ -88,7 +88,7 @@ public MultiDocumentEntity> insertDocuments(final Ite @Override public MultiDocumentEntity> insertDocuments( final Iterable values, final DocumentCreateOptions options) { - return executor + return executorSync() .execute(insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer(Void.class)); } @@ -97,7 +97,7 @@ public MultiDocumentEntity> insertDocuments( public MultiDocumentEntity> insertDocuments(Iterable values, DocumentCreateOptions options, Class type) { - return executor + return executorSync() .execute(insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer(type)); } @@ -108,7 +108,7 @@ public DocumentImportEntity importDocuments(final Iterable values) { @Override public DocumentImportEntity importDocuments(final Iterable values, final DocumentImportOptions options) { - return executor.execute(importDocumentsRequest(values, options), DocumentImportEntity.class); + return executorSync().execute(importDocumentsRequest(values, options), DocumentImportEntity.class); } @Override @@ -118,7 +118,7 @@ public DocumentImportEntity importDocuments(RawData values) { @Override public DocumentImportEntity importDocuments(RawData values, DocumentImportOptions options) { - return executor.execute(importDocumentsRequest(values, options), DocumentImportEntity.class); + return executorSync().execute(importDocumentsRequest(values, options), DocumentImportEntity.class); } @Override @@ -130,7 +130,7 @@ public T getDocument(final String key, final Class type) { public T getDocument(final String key, final Class type, final DocumentReadOptions options) { DocumentUtil.validateDocumentKey(key); try { - return executor.execute(getDocumentRequest(key, options), getDocumentResponseDeserializer(type)); + return executorSync().execute(getDocumentRequest(key, options), getDocumentResponseDeserializer(type)); } catch (final ArangoDBException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(e.getMessage(), e); @@ -157,12 +157,12 @@ public MultiDocumentEntity getDocuments(final Iterable keys, fina @Override public MultiDocumentEntity getDocuments( final Iterable keys, final Class type, final DocumentReadOptions options) { - return executor.execute(getDocumentsRequest(keys, options), getDocumentsResponseDeserializer(type)); + return executorSync().execute(getDocumentsRequest(keys, options), getDocumentsResponseDeserializer(type)); } @Override public DocumentUpdateEntity replaceDocument(final String key, final Object value) { - return executor.execute(replaceDocumentRequest(key, value, new DocumentReplaceOptions()), + return executorSync().execute(replaceDocumentRequest(key, value, new DocumentReplaceOptions()), constructParametricType(DocumentUpdateEntity.class, Void.class)); } @@ -176,13 +176,13 @@ public DocumentUpdateEntity replaceDocument( @Override public DocumentUpdateEntity replaceDocument(String key, T value, DocumentReplaceOptions options, Class type) { - return executor.execute(replaceDocumentRequest(key, value, options), + return executorSync().execute(replaceDocumentRequest(key, value, options), constructParametricType(DocumentUpdateEntity.class, type)); } @Override public MultiDocumentEntity> replaceDocuments(RawData values) { - return executor.execute(replaceDocumentsRequest(values, new DocumentReplaceOptions()), + return executorSync().execute(replaceDocumentsRequest(values, new DocumentReplaceOptions()), replaceDocumentsResponseDeserializer(Void.class)); } @@ -190,7 +190,7 @@ public MultiDocumentEntity> replaceDocuments(RawData @SuppressWarnings("unchecked") public MultiDocumentEntity> replaceDocuments(RawData values, DocumentReplaceOptions options) { - return executor.execute(replaceDocumentsRequest(values, options), + return executorSync().execute(replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer((Class) values.getClass())); } @@ -202,7 +202,7 @@ public MultiDocumentEntity> replaceDocuments(final It @Override public MultiDocumentEntity> replaceDocuments( final Iterable values, final DocumentReplaceOptions options) { - return executor.execute(replaceDocumentsRequest(values, options), + return executorSync().execute(replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(Void.class)); } @@ -210,7 +210,7 @@ public MultiDocumentEntity> replaceDocuments( public MultiDocumentEntity> replaceDocuments(Iterable values, DocumentReplaceOptions options, Class type) { - return executor.execute(replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(type)); + return executorSync().execute(replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(type)); } @Override @@ -228,13 +228,13 @@ public DocumentUpdateEntity updateDocument( @Override public DocumentUpdateEntity updateDocument( final String key, final Object value, final DocumentUpdateOptions options, final Class returnType) { - return executor.execute(updateDocumentRequest(key, value, options), + return executorSync().execute(updateDocumentRequest(key, value, options), constructParametricType(DocumentUpdateEntity.class, returnType)); } @Override public MultiDocumentEntity> updateDocuments(RawData values) { - return executor + return executorSync() .execute(updateDocumentsRequest(values, new DocumentUpdateOptions()), updateDocumentsResponseDeserializer(Void.class)); } @@ -243,7 +243,7 @@ public MultiDocumentEntity> updateDocuments(RawData v @SuppressWarnings("unchecked") public MultiDocumentEntity> updateDocuments(RawData values, DocumentUpdateOptions options) { - return executor + return executorSync() .execute(updateDocumentsRequest(values, options), updateDocumentsResponseDeserializer((Class) values.getClass())); } @@ -262,7 +262,7 @@ public MultiDocumentEntity> updateDocuments( @Override public MultiDocumentEntity> updateDocuments( final Iterable values, final DocumentUpdateOptions options, final Class returnType) { - return executor + return executorSync() .execute(updateDocumentsRequest(values, options), updateDocumentsResponseDeserializer(returnType)); } @@ -279,13 +279,13 @@ public DocumentDeleteEntity deleteDocument(String key, DocumentDeleteOptio @Override public DocumentDeleteEntity deleteDocument( final String key, final DocumentDeleteOptions options, final Class type) { - return executor.execute(deleteDocumentRequest(key, options), + return executorSync().execute(deleteDocumentRequest(key, options), constructParametricType(DocumentDeleteEntity.class, type)); } @Override public MultiDocumentEntity> deleteDocuments(RawData values) { - return executor.execute(deleteDocumentsRequest(values, new DocumentDeleteOptions()), + return executorSync().execute(deleteDocumentsRequest(values, new DocumentDeleteOptions()), deleteDocumentsResponseDeserializer(Void.class)); } @@ -293,7 +293,7 @@ public MultiDocumentEntity> deleteDocuments(RawData v @SuppressWarnings("unchecked") public MultiDocumentEntity> deleteDocuments(RawData values, DocumentDeleteOptions options) { - return executor.execute(deleteDocumentsRequest(values, options), + return executorSync().execute(deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer((Class) values.getClass())); } @@ -311,7 +311,7 @@ public MultiDocumentEntity> deleteDocuments( @Override public MultiDocumentEntity> deleteDocuments( final Iterable values, final DocumentDeleteOptions options, final Class type) { - return executor.execute(deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer(type)); + return executorSync().execute(deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer(type)); } @Override @@ -322,7 +322,7 @@ public Boolean documentExists(final String key) { @Override public Boolean documentExists(final String key, final DocumentExistsOptions options) { try { - executor.execute(documentExistsRequest(key, options), Void.class); + executorSync().execute(documentExistsRequest(key, options), Void.class); return true; } catch (final ArangoDBException e) { @@ -341,58 +341,58 @@ public Boolean documentExists(final String key, final DocumentExistsOptions opti @Override public IndexEntity getIndex(final String id) { - return executor.execute(getIndexRequest(id), IndexEntity.class); + return executorSync().execute(getIndexRequest(id), IndexEntity.class); } @Override public InvertedIndexEntity getInvertedIndex(String id) { - return executor.execute(getIndexRequest(id), InvertedIndexEntity.class); + return executorSync().execute(getIndexRequest(id), InvertedIndexEntity.class); } @Override public String deleteIndex(final String id) { - return executor.execute(deleteIndexRequest(id), deleteIndexResponseDeserializer()); + return executorSync().execute(deleteIndexRequest(id), deleteIndexResponseDeserializer()); } @Override public IndexEntity ensurePersistentIndex(final Iterable fields, final PersistentIndexOptions options) { - return executor.execute(createPersistentIndexRequest(fields, options), IndexEntity.class); + return executorSync().execute(createPersistentIndexRequest(fields, options), IndexEntity.class); } @Override public InvertedIndexEntity ensureInvertedIndex(final InvertedIndexOptions options) { - return executor.execute(createInvertedIndexRequest(options), InvertedIndexEntity.class); + return executorSync().execute(createInvertedIndexRequest(options), InvertedIndexEntity.class); } @Override public IndexEntity ensureGeoIndex(final Iterable fields, final GeoIndexOptions options) { - return executor.execute(createGeoIndexRequest(fields, options), IndexEntity.class); + return executorSync().execute(createGeoIndexRequest(fields, options), IndexEntity.class); } @Deprecated @Override public IndexEntity ensureFulltextIndex(final Iterable fields, final FulltextIndexOptions options) { - return executor.execute(createFulltextIndexRequest(fields, options), IndexEntity.class); + return executorSync().execute(createFulltextIndexRequest(fields, options), IndexEntity.class); } @Override public IndexEntity ensureTtlIndex(final Iterable fields, final TtlIndexOptions options) { - return executor.execute(createTtlIndexRequest(fields, options), IndexEntity.class); + return executorSync().execute(createTtlIndexRequest(fields, options), IndexEntity.class); } @Override public IndexEntity ensureZKDIndex(final Iterable fields, final ZKDIndexOptions options) { - return executor.execute(createZKDIndexRequest(fields, options), IndexEntity.class); + return executorSync().execute(createZKDIndexRequest(fields, options), IndexEntity.class); } @Override public Collection getIndexes() { - return executor.execute(getIndexesRequest(), getIndexesResponseDeserializer()); + return executorSync().execute(getIndexesRequest(), getIndexesResponseDeserializer()); } @Override public Collection getInvertedIndexes() { - return executor.execute(getIndexesRequest(), getInvertedIndexesResponseDeserializer()); + return executorSync().execute(getIndexesRequest(), getInvertedIndexesResponseDeserializer()); } @Override @@ -415,7 +415,7 @@ public CollectionEntity truncate() { @Override public CollectionEntity truncate(CollectionTruncateOptions options) { - return executor.execute(truncateRequest(options), CollectionEntity.class); + return executorSync().execute(truncateRequest(options), CollectionEntity.class); } @Override @@ -425,7 +425,7 @@ public CollectionPropertiesEntity count() { @Override public CollectionPropertiesEntity count(CollectionCountOptions options) { - return executor.execute(countRequest(options), CollectionPropertiesEntity.class); + return executorSync().execute(countRequest(options), CollectionPropertiesEntity.class); } @Override @@ -440,62 +440,62 @@ public CollectionEntity create(final CollectionCreateOptions options) { @Override public void drop() { - executor.execute(dropRequest(null), Void.class); + executorSync().execute(dropRequest(null), Void.class); } @Override public void drop(final boolean isSystem) { - executor.execute(dropRequest(isSystem), Void.class); + executorSync().execute(dropRequest(isSystem), Void.class); } @Override public CollectionEntity getInfo() { - return executor.execute(getInfoRequest(), CollectionEntity.class); + return executorSync().execute(getInfoRequest(), CollectionEntity.class); } @Override public CollectionPropertiesEntity getProperties() { - return executor.execute(getPropertiesRequest(), CollectionPropertiesEntity.class); + return executorSync().execute(getPropertiesRequest(), CollectionPropertiesEntity.class); } @Override public CollectionPropertiesEntity changeProperties(final CollectionPropertiesOptions options) { - return executor.execute(changePropertiesRequest(options), CollectionPropertiesEntity.class); + return executorSync().execute(changePropertiesRequest(options), CollectionPropertiesEntity.class); } @Override public CollectionEntity rename(final String newName) { - return executor.execute(renameRequest(newName), CollectionEntity.class); + return executorSync().execute(renameRequest(newName), CollectionEntity.class); } @Override public ShardEntity getResponsibleShard(final Object value) { - return executor.execute(responsibleShardRequest(value), ShardEntity.class); + return executorSync().execute(responsibleShardRequest(value), ShardEntity.class); } @Override public CollectionRevisionEntity getRevision() { - return executor.execute(getRevisionRequest(), CollectionRevisionEntity.class); + return executorSync().execute(getRevisionRequest(), CollectionRevisionEntity.class); } @Override public void grantAccess(final String user, final Permissions permissions) { - executor.execute(grantAccessRequest(user, permissions), Void.class); + executorSync().execute(grantAccessRequest(user, permissions), Void.class); } @Override public void revokeAccess(final String user) { - executor.execute(grantAccessRequest(user, Permissions.NONE), Void.class); + executorSync().execute(grantAccessRequest(user, Permissions.NONE), Void.class); } @Override public void resetAccess(final String user) { - executor.execute(resetAccessRequest(user), Void.class); + executorSync().execute(resetAccessRequest(user), Void.class); } @Override public Permissions getPermissions(final String user) { - return executor.execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); + return executorSync().execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java new file mode 100644 index 000000000..db4511251 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java @@ -0,0 +1,208 @@ +///* +// * DISCLAIMER +// * +// * Copyright 2016 ArangoDB GmbH, Cologne, Germany +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// * +// * Copyright holder is ArangoDB GmbH, Cologne, Germany +// */ +// +//package com.arangodb.internal; +// +//import com.arangodb.*; +//import com.arangodb.entity.*; +//import com.arangodb.internal.config.ArangoConfig; +//import com.arangodb.internal.net.HostHandler; +//import com.arangodb.internal.net.HostResolver; +//import com.arangodb.internal.net.ProtocolProvider; +//import com.arangodb.internal.serde.SerdeUtils; +//import com.arangodb.model.*; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +// +//import java.util.Collection; +// +///** +// * @author Mark Vollmary +// * @author Heiko Kernbach +// * @author Michele Rastelli +// */ +//public class ArangoDBAsyncImpl extends InternalArangoDB implements ArangoDB { +// +// private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBAsyncImpl.class); +// private final HostHandler hostHandler; +// +// public ArangoDBAsyncImpl(final ArangoConfig config, +// final HostResolver hostResolver, final ProtocolProvider protocolProvider, +// final HostHandler hostHandler) { +// super(new ArangoExecutorSync(protocolProvider.createProtocol(config, hostHandler), config), config.getInternalSerde()); +// this.hostHandler = hostHandler; +// hostResolver.init(this.executorSync(), getSerde()); +// LOGGER.debug("ArangoDB Client is ready to use"); +// } +// +// @Override +// public void shutdown() { +// executorSync.disconnect(); +// } +// +// @Override +// public void updateJwt(String jwt) { +// hostHandler.setJwt(jwt); +// executorSync.setJwt(jwt); +// } +// +// @Override +// public ArangoDatabase db() { +// return db(ArangoRequestParam.SYSTEM); +// } +// +// @Override +// public ArangoDatabase db(final String dbName) { +// return new ArangoDatabaseImpl(this, dbName); +// } +// +// @Override +// public ArangoMetrics metrics() { +// return new ArangoMetricsImpl(executorSync.getQueueTimeMetrics()); +// } +// +// @Override +// public Boolean createDatabase(final String dbName) { +// return createDatabase(new DBCreateOptions().name(dbName)); +// } +// +// @Override +// public Boolean createDatabase(DBCreateOptions options) { +// return executorSync.execute(createDatabaseRequest(options), createDatabaseResponseDeserializer()); +// } +// +// @Override +// public Collection getDatabases() { +// return executorSync.execute(getDatabasesRequest(db().name()), getDatabaseResponseDeserializer()); +// } +// +// @Override +// public Collection getAccessibleDatabases() { +// return db().getAccessibleDatabases(); +// } +// +// @Override +// public Collection getAccessibleDatabasesFor(final String user) { +// return executorSync.execute(getAccessibleDatabasesForRequest(db().name(), user), +// getAccessibleDatabasesForResponseDeserializer()); +// } +// +// @Override +// public ArangoDBVersion getVersion() { +// return db().getVersion(); +// } +// +// @Override +// public ArangoDBEngine getEngine() { +// return db().getEngine(); +// } +// +// @Override +// public ServerRole getRole() { +// return executorSync.execute(getRoleRequest(), getRoleResponseDeserializer()); +// } +// +// @Override +// public String getServerId() { +// return executorSync.execute(getServerIdRequest(), getServerIdResponseDeserializer()); +// } +// +// @Override +// public UserEntity createUser(final String user, final String passwd) { +// return executorSync.execute(createUserRequest(db().name(), user, passwd, new UserCreateOptions()), +// UserEntity.class); +// } +// +// @Override +// public UserEntity createUser(final String user, final String passwd, final UserCreateOptions options) { +// return executorSync.execute(createUserRequest(db().name(), user, passwd, options), UserEntity.class); +// } +// +// @Override +// public void deleteUser(final String user) { +// executorSync.execute(deleteUserRequest(db().name(), user), Void.class); +// } +// +// @Override +// public UserEntity getUser(final String user) { +// return executorSync.execute(getUserRequest(db().name(), user), UserEntity.class); +// } +// +// @Override +// public Collection getUsers() { +// return executorSync.execute(getUsersRequest(db().name()), getUsersResponseDeserializer()); +// } +// +// @Override +// public UserEntity updateUser(final String user, final UserUpdateOptions options) { +// return executorSync.execute(updateUserRequest(db().name(), user, options), UserEntity.class); +// } +// +// @Override +// public UserEntity replaceUser(final String user, final UserUpdateOptions options) { +// return executorSync.execute(replaceUserRequest(db().name(), user, options), UserEntity.class); +// } +// +// @Override +// public void grantDefaultDatabaseAccess(final String user, final Permissions permissions) { +// executorSync.execute(updateUserDefaultDatabaseAccessRequest(user, permissions), Void.class); +// } +// +// @Override +// public void grantDefaultCollectionAccess(final String user, final Permissions permissions) { +// executorSync.execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); +// } +// +// @Override +// public Response execute(Request request, Class type) { +// return executorSync.execute(executeRequest(request), responseDeserializer(type)); +// } +// +// @Override +// public LogEntriesEntity getLogEntries(final LogOptions options) { +// return executorSync.execute(getLogEntriesRequest(options), LogEntriesEntity.class); +// } +// +// @Override +// public LogLevelEntity getLogLevel() { +// return getLogLevel(new LogLevelOptions()); +// } +// +// @Override +// public LogLevelEntity getLogLevel(final LogLevelOptions options) { +// return executorSync.execute(getLogLevelRequest(options), LogLevelEntity.class); +// } +// +// @Override +// public LogLevelEntity setLogLevel(final LogLevelEntity entity) { +// return setLogLevel(entity, new LogLevelOptions()); +// } +// +// @Override +// public LogLevelEntity setLogLevel(final LogLevelEntity entity, final LogLevelOptions options) { +// return executorSync.execute(setLogLevelRequest(entity, options), LogLevelEntity.class); +// } +// +// @Override +// public Collection getQueryOptimizerRules() { +// return executorSync.execute(getQueryOptimizerRulesRequest(), SerdeUtils.constructListType(QueryOptimizerRule.class)); +// } +// +//} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java index 711a4dbfd..bfbcb7f54 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java @@ -46,21 +46,21 @@ public class ArangoDBImpl extends InternalArangoDB implements ArangoDB { public ArangoDBImpl(final ArangoConfig config, final HostResolver hostResolver, final ProtocolProvider protocolProvider, final HostHandler hostHandler) { - super(new ArangoExecutorSync(protocolProvider.createProtocol(config, hostHandler), config), config.getInternalSerde()); + super(protocolProvider.createProtocol(config, hostHandler), config, config.getInternalSerde()); this.hostHandler = hostHandler; - hostResolver.init(this.executor(), getSerde()); + hostResolver.init(executorSync(), getSerde()); LOGGER.debug("ArangoDB Client is ready to use"); } @Override public void shutdown() { - executor.disconnect(); + executorSync().disconnect(); } @Override public void updateJwt(String jwt) { hostHandler.setJwt(jwt); - executor.setJwt(jwt); + executorSync().setJwt(jwt); } @Override @@ -75,7 +75,7 @@ public ArangoDatabase db(final String dbName) { @Override public ArangoMetrics metrics() { - return new ArangoMetricsImpl(executor.getQueueTimeMetrics()); + return new ArangoMetricsImpl(executorSync().getQueueTimeMetrics()); } @Override @@ -85,12 +85,12 @@ public Boolean createDatabase(final String dbName) { @Override public Boolean createDatabase(DBCreateOptions options) { - return executor.execute(createDatabaseRequest(options), createDatabaseResponseDeserializer()); + return executorSync().execute(createDatabaseRequest(options), createDatabaseResponseDeserializer()); } @Override public Collection getDatabases() { - return executor.execute(getDatabasesRequest(db().name()), getDatabaseResponseDeserializer()); + return executorSync().execute(getDatabasesRequest(db().name()), getDatabaseResponseDeserializer()); } @Override @@ -100,7 +100,7 @@ public Collection getAccessibleDatabases() { @Override public Collection getAccessibleDatabasesFor(final String user) { - return executor.execute(getAccessibleDatabasesForRequest(db().name(), user), + return executorSync().execute(getAccessibleDatabasesForRequest(db().name(), user), getAccessibleDatabasesForResponseDeserializer()); } @@ -116,68 +116,68 @@ public ArangoDBEngine getEngine() { @Override public ServerRole getRole() { - return executor.execute(getRoleRequest(), getRoleResponseDeserializer()); + return executorSync().execute(getRoleRequest(), getRoleResponseDeserializer()); } @Override public String getServerId() { - return executor.execute(getServerIdRequest(), getServerIdResponseDeserializer()); + return executorSync().execute(getServerIdRequest(), getServerIdResponseDeserializer()); } @Override public UserEntity createUser(final String user, final String passwd) { - return executor.execute(createUserRequest(db().name(), user, passwd, new UserCreateOptions()), + return executorSync().execute(createUserRequest(db().name(), user, passwd, new UserCreateOptions()), UserEntity.class); } @Override public UserEntity createUser(final String user, final String passwd, final UserCreateOptions options) { - return executor.execute(createUserRequest(db().name(), user, passwd, options), UserEntity.class); + return executorSync().execute(createUserRequest(db().name(), user, passwd, options), UserEntity.class); } @Override public void deleteUser(final String user) { - executor.execute(deleteUserRequest(db().name(), user), Void.class); + executorSync().execute(deleteUserRequest(db().name(), user), Void.class); } @Override public UserEntity getUser(final String user) { - return executor.execute(getUserRequest(db().name(), user), UserEntity.class); + return executorSync().execute(getUserRequest(db().name(), user), UserEntity.class); } @Override public Collection getUsers() { - return executor.execute(getUsersRequest(db().name()), getUsersResponseDeserializer()); + return executorSync().execute(getUsersRequest(db().name()), getUsersResponseDeserializer()); } @Override public UserEntity updateUser(final String user, final UserUpdateOptions options) { - return executor.execute(updateUserRequest(db().name(), user, options), UserEntity.class); + return executorSync().execute(updateUserRequest(db().name(), user, options), UserEntity.class); } @Override public UserEntity replaceUser(final String user, final UserUpdateOptions options) { - return executor.execute(replaceUserRequest(db().name(), user, options), UserEntity.class); + return executorSync().execute(replaceUserRequest(db().name(), user, options), UserEntity.class); } @Override public void grantDefaultDatabaseAccess(final String user, final Permissions permissions) { - executor.execute(updateUserDefaultDatabaseAccessRequest(user, permissions), Void.class); + executorSync().execute(updateUserDefaultDatabaseAccessRequest(user, permissions), Void.class); } @Override public void grantDefaultCollectionAccess(final String user, final Permissions permissions) { - executor.execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); + executorSync().execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); } @Override public Response execute(Request request, Class type) { - return executor.execute(executeRequest(request), responseDeserializer(type)); + return executorSync().execute(executeRequest(request), responseDeserializer(type)); } @Override public LogEntriesEntity getLogEntries(final LogOptions options) { - return executor.execute(getLogEntriesRequest(options), LogEntriesEntity.class); + return executorSync().execute(getLogEntriesRequest(options), LogEntriesEntity.class); } @Override @@ -187,7 +187,7 @@ public LogLevelEntity getLogLevel() { @Override public LogLevelEntity getLogLevel(final LogLevelOptions options) { - return executor.execute(getLogLevelRequest(options), LogLevelEntity.class); + return executorSync().execute(getLogLevelRequest(options), LogLevelEntity.class); } @Override @@ -197,12 +197,12 @@ public LogLevelEntity setLogLevel(final LogLevelEntity entity) { @Override public LogLevelEntity setLogLevel(final LogLevelEntity entity, final LogLevelOptions options) { - return executor.execute(setLogLevelRequest(entity, options), LogLevelEntity.class); + return executorSync().execute(setLogLevelRequest(entity, options), LogLevelEntity.class); } @Override public Collection getQueryOptimizerRules() { - return executor.execute(getQueryOptimizerRulesRequest(), SerdeUtils.constructListType(QueryOptimizerRule.class)); + return executorSync().execute(getQueryOptimizerRulesRequest(), SerdeUtils.constructListType(QueryOptimizerRule.class)); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java index 065ad4f67..24422febb 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java @@ -49,12 +49,12 @@ protected ArangoDatabaseImpl(final ArangoDBImpl arangoDB, final String name) { @Override public ArangoDBVersion getVersion() { - return executor.execute(getVersionRequest(), ArangoDBVersion.class); + return executorSync().execute(getVersionRequest(), ArangoDBVersion.class); } @Override public ArangoDBEngine getEngine() { - return executor.execute(getEngineRequest(), ArangoDBEngine.class); + return executorSync().execute(getEngineRequest(), ArangoDBEngine.class); } @Override @@ -72,7 +72,7 @@ public boolean exists() { @Override public Collection getAccessibleDatabases() { - return executor.execute(getAccessibleDatabasesRequest(), getDatabaseResponseDeserializer()); + return executorSync().execute(getAccessibleDatabasesRequest(), getDatabaseResponseDeserializer()); } @Override @@ -82,23 +82,23 @@ public ArangoCollection collection(final String name) { @Override public CollectionEntity createCollection(final String name) { - return executor.execute(createCollectionRequest(name, new CollectionCreateOptions()), CollectionEntity.class); + return executorSync().execute(createCollectionRequest(name, new CollectionCreateOptions()), CollectionEntity.class); } @Override public CollectionEntity createCollection(final String name, final CollectionCreateOptions options) { - return executor.execute(createCollectionRequest(name, options), CollectionEntity.class); + return executorSync().execute(createCollectionRequest(name, options), CollectionEntity.class); } @Override public Collection getCollections() { - return executor + return executorSync() .execute(getCollectionsRequest(new CollectionsReadOptions()), getCollectionsResponseDeserializer()); } @Override public Collection getCollections(final CollectionsReadOptions options) { - return executor.execute(getCollectionsRequest(options), getCollectionsResponseDeserializer()); + return executorSync().execute(getCollectionsRequest(options), getCollectionsResponseDeserializer()); } @Override @@ -122,37 +122,37 @@ public Boolean create() { @Override public Boolean drop() { - return executor.execute(dropRequest(), createDropResponseDeserializer()); + return executorSync().execute(dropRequest(), createDropResponseDeserializer()); } @Override public void grantAccess(final String user, final Permissions permissions) { - executor.execute(grantAccessRequest(user, permissions), Void.class); + executorSync().execute(grantAccessRequest(user, permissions), Void.class); } @Override public void grantAccess(final String user) { - executor.execute(grantAccessRequest(user, Permissions.RW), Void.class); + executorSync().execute(grantAccessRequest(user, Permissions.RW), Void.class); } @Override public void revokeAccess(final String user) { - executor.execute(grantAccessRequest(user, Permissions.NONE), Void.class); + executorSync().execute(grantAccessRequest(user, Permissions.NONE), Void.class); } @Override public void resetAccess(final String user) { - executor.execute(resetAccessRequest(user), Void.class); + executorSync().execute(resetAccessRequest(user), Void.class); } @Override public void grantDefaultCollectionAccess(final String user, final Permissions permissions) { - executor.execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); + executorSync().execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); } @Override public Permissions getPermissions(final String user) { - return executor.execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); + return executorSync().execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); } @Override @@ -160,7 +160,7 @@ public ArangoCursor query( final String query, final Class type, final Map bindVars, final AqlQueryOptions options) { final InternalRequest request = queryRequest(query, bindVars, options); final HostHandle hostHandle = new HostHandle(); - final InternalCursorEntity result = executor.execute(request, internalCursorEntityDeserializer(), hostHandle); + final InternalCursorEntity result = executorSync().execute(request, internalCursorEntityDeserializer(), hostHandle); return createCursor(result, type, options, hostHandle); } @@ -182,7 +182,7 @@ public ArangoCursor query(final String query, final Class type) { @Override public ArangoCursor cursor(final String cursorId, final Class type) { final HostHandle hostHandle = new HostHandle(); - final InternalCursorEntity result = executor.execute( + final InternalCursorEntity result = executorSync().execute( queryNextRequest(cursorId, null), internalCursorEntityDeserializer(), hostHandle); @@ -192,7 +192,7 @@ public ArangoCursor cursor(final String cursorId, final Class type) { @Override public ArangoCursor cursor(final String cursorId, final Class type, final String nextBatchId) { final HostHandle hostHandle = new HostHandle(); - final InternalCursorEntity result = executor.execute( + final InternalCursorEntity result = executorSync().execute( queryNextByBatchIdRequest(cursorId, nextBatchId, null), internalCursorEntityDeserializer(), hostHandle); @@ -210,12 +210,12 @@ private ArangoCursor createCursor( public InternalCursorEntity next(final String id, final String nextBatchId) { InternalRequest request = nextBatchId == null ? queryNextRequest(id, options) : queryNextByBatchIdRequest(id, nextBatchId, options); - return executor.execute(request, internalCursorEntityDeserializer(), hostHandle); + return executorSync().execute(request, internalCursorEntityDeserializer(), hostHandle); } @Override public void close(final String id) { - executor.execute(queryCloseRequest(id, options), Void.class, hostHandle); + executorSync().execute(queryCloseRequest(id, options), Void.class, hostHandle); } }; @@ -225,75 +225,75 @@ public void close(final String id) { @Override public AqlExecutionExplainEntity explainQuery( final String query, final Map bindVars, final AqlQueryExplainOptions options) { - return executor.execute(explainQueryRequest(query, bindVars, options), AqlExecutionExplainEntity.class); + return executorSync().execute(explainQueryRequest(query, bindVars, options), AqlExecutionExplainEntity.class); } @Override public AqlParseEntity parseQuery(final String query) { - return executor.execute(parseQueryRequest(query), AqlParseEntity.class); + return executorSync().execute(parseQueryRequest(query), AqlParseEntity.class); } @Override public void clearQueryCache() { - executor.execute(clearQueryCacheRequest(), Void.class); + executorSync().execute(clearQueryCacheRequest(), Void.class); } @Override public QueryCachePropertiesEntity getQueryCacheProperties() { - return executor.execute(getQueryCachePropertiesRequest(), QueryCachePropertiesEntity.class); + return executorSync().execute(getQueryCachePropertiesRequest(), QueryCachePropertiesEntity.class); } @Override public QueryCachePropertiesEntity setQueryCacheProperties(final QueryCachePropertiesEntity properties) { - return executor.execute(setQueryCachePropertiesRequest(properties), QueryCachePropertiesEntity.class); + return executorSync().execute(setQueryCachePropertiesRequest(properties), QueryCachePropertiesEntity.class); } @Override public QueryTrackingPropertiesEntity getQueryTrackingProperties() { - return executor.execute(getQueryTrackingPropertiesRequest(), QueryTrackingPropertiesEntity.class); + return executorSync().execute(getQueryTrackingPropertiesRequest(), QueryTrackingPropertiesEntity.class); } @Override public QueryTrackingPropertiesEntity setQueryTrackingProperties(final QueryTrackingPropertiesEntity properties) { - return executor.execute(setQueryTrackingPropertiesRequest(properties), QueryTrackingPropertiesEntity.class); + return executorSync().execute(setQueryTrackingPropertiesRequest(properties), QueryTrackingPropertiesEntity.class); } @Override public Collection getCurrentlyRunningQueries() { - return executor.execute(getCurrentlyRunningQueriesRequest(), + return executorSync().execute(getCurrentlyRunningQueriesRequest(), constructListType(QueryEntity.class)); } @Override public Collection getSlowQueries() { - return executor.execute(getSlowQueriesRequest(), + return executorSync().execute(getSlowQueriesRequest(), constructListType(QueryEntity.class)); } @Override public void clearSlowQueries() { - executor.execute(clearSlowQueriesRequest(), Void.class); + executorSync().execute(clearSlowQueriesRequest(), Void.class); } @Override public void killQuery(final String id) { - executor.execute(killQueryRequest(id), Void.class); + executorSync().execute(killQueryRequest(id), Void.class); } @Override public void createAqlFunction( final String name, final String code, final AqlFunctionCreateOptions options) { - executor.execute(createAqlFunctionRequest(name, code, options), Void.class); + executorSync().execute(createAqlFunctionRequest(name, code, options), Void.class); } @Override public Integer deleteAqlFunction(final String name, final AqlFunctionDeleteOptions options) { - return executor.execute(deleteAqlFunctionRequest(name, options), deleteAqlFunctionResponseDeserializer()); + return executorSync().execute(deleteAqlFunctionRequest(name, options), deleteAqlFunctionResponseDeserializer()); } @Override public Collection getAqlFunctions(final AqlFunctionGetOptions options) { - return executor.execute(getAqlFunctionsRequest(options), getAqlFunctionsResponseDeserializer()); + return executorSync().execute(getAqlFunctionsRequest(options), getAqlFunctionsResponseDeserializer()); } @Override @@ -309,57 +309,57 @@ public GraphEntity createGraph(final String name, final Iterable @Override public GraphEntity createGraph( final String name, final Iterable edgeDefinitions, final GraphCreateOptions options) { - return executor.execute(createGraphRequest(name, edgeDefinitions, options), createGraphResponseDeserializer()); + return executorSync().execute(createGraphRequest(name, edgeDefinitions, options), createGraphResponseDeserializer()); } @Override public Collection getGraphs() { - return executor.execute(getGraphsRequest(), getGraphsResponseDeserializer()); + return executorSync().execute(getGraphsRequest(), getGraphsResponseDeserializer()); } @Override public T transaction(final String action, final Class type, final TransactionOptions options) { - return executor.execute(transactionRequest(action, options), transactionResponseDeserializer(type)); + return executorSync().execute(transactionRequest(action, options), transactionResponseDeserializer(type)); } @Override public StreamTransactionEntity beginStreamTransaction(StreamTransactionOptions options) { - return executor.execute(beginStreamTransactionRequest(options), streamTransactionResponseDeserializer()); + return executorSync().execute(beginStreamTransactionRequest(options), streamTransactionResponseDeserializer()); } @Override public StreamTransactionEntity abortStreamTransaction(String id) { - return executor.execute(abortStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + return executorSync().execute(abortStreamTransactionRequest(id), streamTransactionResponseDeserializer()); } @Override public StreamTransactionEntity getStreamTransaction(String id) { - return executor.execute(getStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + return executorSync().execute(getStreamTransactionRequest(id), streamTransactionResponseDeserializer()); } @Override public Collection getStreamTransactions() { - return executor.execute(getStreamTransactionsRequest(), transactionsResponseDeserializer()); + return executorSync().execute(getStreamTransactionsRequest(), transactionsResponseDeserializer()); } @Override public StreamTransactionEntity commitStreamTransaction(String id) { - return executor.execute(commitStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + return executorSync().execute(commitStreamTransactionRequest(id), streamTransactionResponseDeserializer()); } @Override public DatabaseEntity getInfo() { - return executor.execute(getInfoRequest(), getInfoResponseDeserializer()); + return executorSync().execute(getInfoRequest(), getInfoResponseDeserializer()); } @Override public void reloadRouting() { - executor.execute(reloadRoutingRequest(), Void.class); + executorSync().execute(reloadRoutingRequest(), Void.class); } @Override public Collection getViews() { - return executor.execute(getViewsRequest(), getViewsResponseDeserializer()); + return executorSync().execute(getViewsRequest(), getViewsResponseDeserializer()); } @Override @@ -379,32 +379,32 @@ public SearchAlias searchAlias(String name) { @Override public ViewEntity createView(final String name, final ViewType type) { - return executor.execute(createViewRequest(name, type), ViewEntity.class); + return executorSync().execute(createViewRequest(name, type), ViewEntity.class); } @Override public ViewEntity createArangoSearch(final String name, final ArangoSearchCreateOptions options) { - return executor.execute(createArangoSearchRequest(name, options), ViewEntity.class); + return executorSync().execute(createArangoSearchRequest(name, options), ViewEntity.class); } @Override public ViewEntity createSearchAlias(String name, SearchAliasCreateOptions options) { - return executor.execute(createSearchAliasRequest(name, options), ViewEntity.class); + return executorSync().execute(createSearchAliasRequest(name, options), ViewEntity.class); } @Override public SearchAnalyzer createSearchAnalyzer(SearchAnalyzer analyzer) { - return executor.execute(createAnalyzerRequest(analyzer), SearchAnalyzer.class); + return executorSync().execute(createAnalyzerRequest(analyzer), SearchAnalyzer.class); } @Override public SearchAnalyzer getSearchAnalyzer(String name) { - return executor.execute(getAnalyzerRequest(name), SearchAnalyzer.class); + return executorSync().execute(getAnalyzerRequest(name), SearchAnalyzer.class); } @Override public Collection getSearchAnalyzers() { - return executor.execute(getAnalyzersRequest(), getSearchAnalyzersResponseDeserializer()); + return executorSync().execute(getAnalyzersRequest(), getSearchAnalyzersResponseDeserializer()); } @Override @@ -414,7 +414,7 @@ public void deleteSearchAnalyzer(String name) { @Override public void deleteSearchAnalyzer(String name, AnalyzerDeleteOptions options) { - executor.execute(deleteAnalyzerRequest(name, options), Void.class); + executorSync().execute(deleteAnalyzerRequest(name, options), Void.class); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java index b0fdea6b8..a0ed64185 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java @@ -46,24 +46,24 @@ public void drop() { @Override public void drop(final EdgeCollectionDropOptions options) { - executor.execute(removeEdgeDefinitionRequest(options), Void.class); + executorSync().execute(removeEdgeDefinitionRequest(options), Void.class); } @Override public EdgeEntity insertEdge(final Object value) { - return executor.execute(insertEdgeRequest(value, new EdgeCreateOptions()), + return executorSync().execute(insertEdgeRequest(value, new EdgeCreateOptions()), insertEdgeResponseDeserializer()); } @Override public EdgeEntity insertEdge(final Object value, final EdgeCreateOptions options) { - return executor.execute(insertEdgeRequest(value, options), insertEdgeResponseDeserializer()); + return executorSync().execute(insertEdgeRequest(value, options), insertEdgeResponseDeserializer()); } @Override public T getEdge(final String key, final Class type) { try { - return executor.execute(getEdgeRequest(key, new GraphDocumentReadOptions()), + return executorSync().execute(getEdgeRequest(key, new GraphDocumentReadOptions()), getEdgeResponseDeserializer(type)); } catch (final ArangoDBException e) { if (LOGGER.isDebugEnabled()) { @@ -76,7 +76,7 @@ public T getEdge(final String key, final Class type) { @Override public T getEdge(final String key, final Class type, final GraphDocumentReadOptions options) { try { - return executor.execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)); + return executorSync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)); } catch (final ArangoDBException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(e.getMessage(), e); @@ -87,34 +87,34 @@ public T getEdge(final String key, final Class type, final GraphDocumentR @Override public EdgeUpdateEntity replaceEdge(final String key, final Object value) { - return executor.execute(replaceEdgeRequest(key, value, new EdgeReplaceOptions()), + return executorSync().execute(replaceEdgeRequest(key, value, new EdgeReplaceOptions()), replaceEdgeResponseDeserializer()); } @Override public EdgeUpdateEntity replaceEdge(final String key, final Object value, final EdgeReplaceOptions options) { - return executor.execute(replaceEdgeRequest(key, value, options), replaceEdgeResponseDeserializer()); + return executorSync().execute(replaceEdgeRequest(key, value, options), replaceEdgeResponseDeserializer()); } @Override public EdgeUpdateEntity updateEdge(final String key, final Object value) { - return executor.execute(updateEdgeRequest(key, value, new EdgeUpdateOptions()), + return executorSync().execute(updateEdgeRequest(key, value, new EdgeUpdateOptions()), updateEdgeResponseDeserializer()); } @Override public EdgeUpdateEntity updateEdge(final String key, final Object value, final EdgeUpdateOptions options) { - return executor.execute(updateEdgeRequest(key, value, options), updateEdgeResponseDeserializer()); + return executorSync().execute(updateEdgeRequest(key, value, options), updateEdgeResponseDeserializer()); } @Override public void deleteEdge(final String key) { - executor.execute(deleteEdgeRequest(key, new EdgeDeleteOptions()), Void.class); + executorSync().execute(deleteEdgeRequest(key, new EdgeDeleteOptions()), Void.class); } @Override public void deleteEdge(final String key, final EdgeDeleteOptions options) { - executor.execute(deleteEdgeRequest(key, options), Void.class); + executorSync().execute(deleteEdgeRequest(key, options), Void.class); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecuteableSync.java b/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java similarity index 59% rename from core/src/main/java/com/arangodb/internal/ArangoExecuteableSync.java rename to core/src/main/java/com/arangodb/internal/ArangoExecuteable.java index 5fb9e3e2d..f8b9fabe6 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecuteableSync.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java @@ -21,25 +21,41 @@ package com.arangodb.internal; import com.arangodb.ArangoSerdeAccessor; +import com.arangodb.internal.config.ArangoConfig; +import com.arangodb.internal.net.CommunicationProtocol; import com.arangodb.internal.serde.InternalSerde; import com.arangodb.internal.util.EncodeUtils; /** * @author Mark Vollmary */ -public abstract class ArangoExecuteableSync implements ArangoSerdeAccessor { +public abstract class ArangoExecuteable implements ArangoSerdeAccessor { private static final String SLASH = "/"; - protected final ArangoExecutorSync executor; - protected final InternalSerde serde; + private final ArangoExecutorSync executorSync; + private final ArangoExecutorAsync executorAsync; + private final InternalSerde serde; - protected ArangoExecuteableSync(final ArangoExecutorSync executor, final InternalSerde serde) { - super(); - this.executor = executor; + protected ArangoExecuteable(final CommunicationProtocol protocol, + final ArangoConfig config, + final InternalSerde serde) { + this(new ArangoExecutorSync(protocol, config), new ArangoExecutorAsync(protocol, config), serde); + } + + protected ArangoExecuteable(final ArangoExecuteable other) { + this(other.executorSync, other.executorAsync, other.serde); + } + + private ArangoExecuteable(final ArangoExecutorSync executorSync, + final ArangoExecutorAsync executorAsync, + final InternalSerde serde) { + this.executorSync = executorSync; + this.executorAsync = executorAsync; this.serde = serde; } + protected static String createPath(final String... params) { final StringBuilder sb = new StringBuilder(); for (int i = 0; i < params.length; i++) { @@ -57,8 +73,12 @@ protected static String createPath(final String... params) { return sb.toString(); } - protected ArangoExecutorSync executor() { - return executor; + protected ArangoExecutorSync executorSync() { + return executorSync; + } + + protected ArangoExecutorAsync executorAsync() { + return executorAsync; } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java index d70853e8b..6e63fcfe5 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java @@ -66,22 +66,22 @@ public GraphEntity create(final Iterable edgeDefinitions, final @Override public void drop() { - executor.execute(dropRequest(), Void.class); + executorSync().execute(dropRequest(), Void.class); } @Override public void drop(final boolean dropCollections) { - executor.execute(dropRequest(dropCollections), Void.class); + executorSync().execute(dropRequest(dropCollections), Void.class); } @Override public GraphEntity getInfo() { - return executor.execute(getInfoRequest(), getInfoResponseDeserializer()); + return executorSync().execute(getInfoRequest(), getInfoResponseDeserializer()); } @Override public Collection getVertexCollections() { - return executor.execute(getVertexCollectionsRequest(), getVertexCollectionsResponseDeserializer()); + return executorSync().execute(getVertexCollectionsRequest(), getVertexCollectionsResponseDeserializer()); } @Override @@ -91,7 +91,7 @@ public GraphEntity addVertexCollection(final String name) { @Override public GraphEntity addVertexCollection(final String name, final VertexCollectionCreateOptions options) { - return executor.execute(addVertexCollectionRequest(name, options), addVertexCollectionResponseDeserializer()); + return executorSync().execute(addVertexCollectionRequest(name, options), addVertexCollectionResponseDeserializer()); } @Override @@ -106,12 +106,12 @@ public ArangoEdgeCollection edgeCollection(final String name) { @Override public Collection getEdgeDefinitions() { - return executor.execute(getEdgeDefinitionsRequest(), getEdgeDefinitionsDeserializer()); + return executorSync().execute(getEdgeDefinitionsRequest(), getEdgeDefinitionsDeserializer()); } @Override public GraphEntity addEdgeDefinition(final EdgeDefinition definition) { - return executor.execute(addEdgeDefinitionRequest(definition), addEdgeDefinitionResponseDeserializer()); + return executorSync().execute(addEdgeDefinitionRequest(definition), addEdgeDefinitionResponseDeserializer()); } @Override @@ -121,7 +121,7 @@ public GraphEntity replaceEdgeDefinition(final EdgeDefinition definition) { @Override public GraphEntity replaceEdgeDefinition(final EdgeDefinition definition, final ReplaceEdgeDefinitionOptions options) { - return executor.execute(replaceEdgeDefinitionRequest(definition, options), replaceEdgeDefinitionResponseDeserializer()); + return executorSync().execute(replaceEdgeDefinitionRequest(definition, options), replaceEdgeDefinitionResponseDeserializer()); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java b/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java index 7d66ff087..12610654e 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java @@ -51,17 +51,17 @@ public boolean exists() { @Override public void drop() { - executor.execute(dropRequest(), Void.class); + executorSync().execute(dropRequest(), Void.class); } @Override public ViewEntity rename(final String newName) { - return executor.execute(renameRequest(newName), ViewEntity.class); + return executorSync().execute(renameRequest(newName), ViewEntity.class); } @Override public ViewEntity getInfo() { - return executor.execute(getInfoRequest(), ViewEntity.class); + return executorSync().execute(getInfoRequest(), ViewEntity.class); } @Override @@ -76,17 +76,17 @@ public ViewEntity create(final ArangoSearchCreateOptions options) { @Override public ArangoSearchPropertiesEntity getProperties() { - return executor.execute(getPropertiesRequest(), ArangoSearchPropertiesEntity.class); + return executorSync().execute(getPropertiesRequest(), ArangoSearchPropertiesEntity.class); } @Override public ArangoSearchPropertiesEntity updateProperties(final ArangoSearchPropertiesOptions options) { - return executor.execute(updatePropertiesRequest(options), ArangoSearchPropertiesEntity.class); + return executorSync().execute(updatePropertiesRequest(options), ArangoSearchPropertiesEntity.class); } @Override public ArangoSearchPropertiesEntity replaceProperties(final ArangoSearchPropertiesOptions options) { - return executor.execute(replacePropertiesRequest(options), ArangoSearchPropertiesEntity.class); + return executorSync().execute(replacePropertiesRequest(options), ArangoSearchPropertiesEntity.class); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java index 7bcdb6a34..2a4cdddf8 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java @@ -46,24 +46,24 @@ public void drop() { @Override public void drop(final VertexCollectionDropOptions options) { - executor.execute(dropRequest(options), Void.class); + executorSync().execute(dropRequest(options), Void.class); } @Override public VertexEntity insertVertex(final Object value) { - return executor.execute(insertVertexRequest(value, new VertexCreateOptions()), + return executorSync().execute(insertVertexRequest(value, new VertexCreateOptions()), insertVertexResponseDeserializer()); } @Override public VertexEntity insertVertex(final Object value, final VertexCreateOptions options) { - return executor.execute(insertVertexRequest(value, options), insertVertexResponseDeserializer()); + return executorSync().execute(insertVertexRequest(value, options), insertVertexResponseDeserializer()); } @Override public T getVertex(final String key, final Class type) { try { - return executor.execute(getVertexRequest(key, new GraphDocumentReadOptions()), + return executorSync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), getVertexResponseDeserializer(type)); } catch (final ArangoDBException e) { if (LOGGER.isDebugEnabled()) { @@ -76,7 +76,7 @@ public T getVertex(final String key, final Class type) { @Override public T getVertex(final String key, final Class type, final GraphDocumentReadOptions options) { try { - return executor.execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); + return executorSync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); } catch (final ArangoDBException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(e.getMessage(), e); @@ -87,34 +87,34 @@ public T getVertex(final String key, final Class type, final GraphDocumen @Override public VertexUpdateEntity replaceVertex(final String key, final Object value) { - return executor.execute(replaceVertexRequest(key, value, new VertexReplaceOptions()), + return executorSync().execute(replaceVertexRequest(key, value, new VertexReplaceOptions()), replaceVertexResponseDeserializer()); } @Override public VertexUpdateEntity replaceVertex(final String key, final Object value, final VertexReplaceOptions options) { - return executor.execute(replaceVertexRequest(key, value, options), replaceVertexResponseDeserializer()); + return executorSync().execute(replaceVertexRequest(key, value, options), replaceVertexResponseDeserializer()); } @Override public VertexUpdateEntity updateVertex(final String key, final Object value) { - return executor.execute(updateVertexRequest(key, value, new VertexUpdateOptions()), + return executorSync().execute(updateVertexRequest(key, value, new VertexUpdateOptions()), updateVertexResponseDeserializer()); } @Override public VertexUpdateEntity updateVertex(final String key, final Object value, final VertexUpdateOptions options) { - return executor.execute(updateVertexRequest(key, value, options), updateVertexResponseDeserializer()); + return executorSync().execute(updateVertexRequest(key, value, options), updateVertexResponseDeserializer()); } @Override public void deleteVertex(final String key) { - executor.execute(deleteVertexRequest(key, new VertexDeleteOptions()), Void.class); + executorSync().execute(deleteVertexRequest(key, new VertexDeleteOptions()), Void.class); } @Override public void deleteVertex(final String key, final VertexDeleteOptions options) { - executor.execute(deleteVertexRequest(key, options), Void.class); + executorSync().execute(deleteVertexRequest(key, options), Void.class); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java b/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java index 8a3df3b56..d5e0bf2ed 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java @@ -48,17 +48,17 @@ public boolean exists() { @Override public void drop() { - executor.execute(dropRequest(), Void.class); + executorSync().execute(dropRequest(), Void.class); } @Override public ViewEntity rename(final String newName) { - return executor.execute(renameRequest(newName), ViewEntity.class); + return executorSync().execute(renameRequest(newName), ViewEntity.class); } @Override public ViewEntity getInfo() { - return executor.execute(getInfoRequest(), ViewEntity.class); + return executorSync().execute(getInfoRequest(), ViewEntity.class); } } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java index 340070ba2..5f6aa2777 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java @@ -40,7 +40,7 @@ * @author Mark Vollmary * @author Michele Rastelli */ -public abstract class InternalArangoCollection extends ArangoExecuteableSync { +public abstract class InternalArangoCollection extends ArangoExecuteable { protected static final String PATH_API_COLLECTION = "/_api/collection"; private static final String COLLECTION = "collection"; @@ -64,7 +64,7 @@ public abstract class InternalArangoCollection extends ArangoExecuteableSync { protected final String name; protected InternalArangoCollection(final ArangoDatabaseImpl db, final String name) { - super(db.executor, db.serde); + super(db); this.db = db; this.name = name; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java index dd537c925..372c4f988 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java @@ -27,6 +27,8 @@ import com.arangodb.entity.ServerRole; import com.arangodb.entity.UserEntity; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; +import com.arangodb.internal.config.ArangoConfig; +import com.arangodb.internal.net.CommunicationProtocol; import com.arangodb.internal.serde.InternalSerde; import com.arangodb.model.*; @@ -40,7 +42,7 @@ * @author Mark Vollmary * @author Heiko Kernbach */ -public abstract class InternalArangoDB extends ArangoExecuteableSync { +public abstract class InternalArangoDB extends ArangoExecuteable { private static final String PATH_API_ADMIN_LOG_ENTRIES = "/_admin/log/entries"; private static final String PATH_API_ADMIN_LOG_LEVEL = "/_admin/log/level"; private static final String PATH_API_ROLE = "/_admin/server/role"; @@ -48,8 +50,8 @@ public abstract class InternalArangoDB extends ArangoExecuteableSync { private static final String PATH_API_USER = "/_api/user"; private static final String PATH_API_QUERY_RULES = "/_api/query/rules"; - protected InternalArangoDB(final ArangoExecutorSync executor, final InternalSerde util) { - super(executor, util); + protected InternalArangoDB(final CommunicationProtocol protocol, final ArangoConfig config, final InternalSerde util) { + super(protocol, config, util); } protected InternalRequest getRoleRequest() { @@ -163,7 +165,7 @@ protected InternalRequest executeRequest(final Request request) { InternalRequest ireq = new InternalRequest(request.getDb(), RequestType.from(request.getMethod()), request.getPath()); ireq.putHeaderParams(request.getHeaders()); ireq.putQueryParams(request.getQueryParams()); - ireq.setBody(serde.serializeUserData(request.getBody())); + ireq.setBody(getSerde().serializeUserData(request.getBody())); return ireq; } @@ -171,7 +173,7 @@ protected ResponseDeserializer> responseDeserializer(Class ty return response -> new Response<>( response.getResponseCode(), response.getMeta(), - serde.deserializeUserData(response.getBody(), type) + getSerde().deserializeUserData(response.getBody(), type) ); } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java index 2212ff55f..f8653c77f 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java @@ -38,7 +38,7 @@ * @author Mark Vollmary * @author Michele Rastelli */ -public abstract class InternalArangoDatabase extends ArangoExecuteableSync { +public abstract class InternalArangoDatabase extends ArangoExecuteable { protected static final String PATH_API_DATABASE = "/_api/database"; private static final String PATH_API_VERSION = "/_api/version"; @@ -63,7 +63,7 @@ public abstract class InternalArangoDatabase extends ArangoExecuteableSync { private final ArangoDBImpl arango; protected InternalArangoDatabase(final ArangoDBImpl arango, final String name) { - super(arango.executor, arango.serde); + super(arango); this.arango = arango; this.name = name; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoEdgeCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoEdgeCollection.java index a3c3a67b5..9c65f23d5 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoEdgeCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoEdgeCollection.java @@ -31,7 +31,7 @@ /** * @author Mark Vollmary */ -public abstract class InternalArangoEdgeCollection extends ArangoExecuteableSync { +public abstract class InternalArangoEdgeCollection extends ArangoExecuteable { private static final String PATH_API_GHARIAL = "/_api/gharial"; private static final String TRANSACTION_ID = "x-arango-trx-id"; @@ -42,7 +42,7 @@ public abstract class InternalArangoEdgeCollection extends ArangoExecuteableSync private final String name; protected InternalArangoEdgeCollection(final ArangoGraphImpl graph, final String name) { - super(graph.executor, graph.serde); + super(graph); this.graph = graph; this.name = name; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java b/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java index 4fd2bd601..1b941cb21 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java @@ -35,7 +35,7 @@ /** * @author Mark Vollmary */ -public abstract class InternalArangoGraph extends ArangoExecuteableSync { +public abstract class InternalArangoGraph extends ArangoExecuteable { protected static final String PATH_API_GHARIAL = "/_api/gharial"; private static final String GRAPH = "/graph"; @@ -46,7 +46,7 @@ public abstract class InternalArangoGraph extends ArangoExecuteableSync { private final String name; protected InternalArangoGraph(final ArangoDatabaseImpl db, final String name) { - super(db.executor, db.serde); + super(db); this.db = db; this.name = name; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java index 37a63f3c1..db25e9046 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java @@ -31,7 +31,7 @@ /** * @author Mark Vollmary */ -public abstract class InternalArangoVertexCollection extends ArangoExecuteableSync { +public abstract class InternalArangoVertexCollection extends ArangoExecuteable { private static final String PATH_API_GHARIAL = "/_api/gharial"; private static final String VERTEX_PATH = "vertex"; @@ -42,7 +42,7 @@ public abstract class InternalArangoVertexCollection extends ArangoExecuteableSy private final String name; protected InternalArangoVertexCollection(final ArangoGraphImpl graph, final String name) { - super(graph.executor, graph.serde); + super(graph); this.graph = graph; this.name = name; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoView.java b/core/src/main/java/com/arangodb/internal/InternalArangoView.java index 2476f83dc..1aa4ba29d 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoView.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoView.java @@ -28,7 +28,7 @@ * @author Mark Vollmary * @author Michele Rastelli */ -public abstract class InternalArangoView extends ArangoExecuteableSync { +public abstract class InternalArangoView extends ArangoExecuteable { protected static final String PATH_API_VIEW = "/_api/view"; protected static final String PATH_API_ANALYZER = "/_api/analyzer"; @@ -37,7 +37,7 @@ public abstract class InternalArangoView extends ArangoExecuteableSync { protected final String name; protected InternalArangoView(final ArangoDatabaseImpl db, final String name) { - super(db.executor, db.serde); + super(db); this.db = db; this.name = name; } diff --git a/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java b/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java index 10fd33b25..b7839098a 100644 --- a/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java +++ b/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java @@ -51,17 +51,17 @@ public boolean exists() { @Override public void drop() { - executor.execute(dropRequest(), Void.class); + executorSync().execute(dropRequest(), Void.class); } @Override public ViewEntity rename(final String newName) { - return executor.execute(renameRequest(newName), ViewEntity.class); + return executorSync().execute(renameRequest(newName), ViewEntity.class); } @Override public ViewEntity getInfo() { - return executor.execute(getInfoRequest(), ViewEntity.class); + return executorSync().execute(getInfoRequest(), ViewEntity.class); } @Override @@ -76,17 +76,17 @@ public ViewEntity create(final SearchAliasCreateOptions options) { @Override public SearchAliasPropertiesEntity getProperties() { - return executor.execute(getPropertiesRequest(), SearchAliasPropertiesEntity.class); + return executorSync().execute(getPropertiesRequest(), SearchAliasPropertiesEntity.class); } @Override public SearchAliasPropertiesEntity updateProperties(final SearchAliasPropertiesOptions options) { - return executor.execute(updatePropertiesRequest(options), SearchAliasPropertiesEntity.class); + return executorSync().execute(updatePropertiesRequest(options), SearchAliasPropertiesEntity.class); } @Override public SearchAliasPropertiesEntity replaceProperties(final SearchAliasPropertiesOptions options) { - return executor.execute(replacePropertiesRequest(options), SearchAliasPropertiesEntity.class); + return executorSync().execute(replacePropertiesRequest(options), SearchAliasPropertiesEntity.class); } } From 6a09ec7e64f03294865e4984a3ae599369f13b04 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 11 Oct 2023 13:11:46 +0200 Subject: [PATCH 14/62] ArangoExecuteable refactoring --- .../main/java/com/arangodb/ArangoDBAsync.java | 181 +++++++ .../internal/ArangoCollectionAsyncImpl.java | 502 ------------------ 2 files changed, 181 insertions(+), 502 deletions(-) create mode 100644 core/src/main/java/com/arangodb/ArangoDBAsync.java delete mode 100644 core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java diff --git a/core/src/main/java/com/arangodb/ArangoDBAsync.java b/core/src/main/java/com/arangodb/ArangoDBAsync.java new file mode 100644 index 000000000..8ce4ef2a1 --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoDBAsync.java @@ -0,0 +1,181 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.model.*; + +import javax.annotation.concurrent.ThreadSafe; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +/** + * Asynchronous version of {@link ArangoDB} + */ +@ThreadSafe +public interface ArangoDBAsync extends ArangoSerdeAccessor { + +// /** +// * Returns a {@code ArangoDatabase} instance for the {@code _system} database. +// * +// * @return database handler +// */ +// ArangoDatabase db(); +// +// /** +// * Returns a {@code ArangoDatabase} instance for the given database name. +// * +// * @param name Name of the database +// * @return database handler +// */ +// ArangoDatabase db(String name); +// +// /** +// * @return entry point for accessing client metrics +// */ +// ArangoMetrics metrics(); + + /** + * Asynchronous version of {@link ArangoDB#createDatabase(String)} + */ + CompletableFuture createDatabase(String name); + + /** + * Asynchronous version of {@link ArangoDB#createDatabase(DBCreateOptions)} + */ + CompletableFuture createDatabase(DBCreateOptions options); + + /** + * Asynchronous version of {@link ArangoDB#getDatabases()} + */ + CompletableFuture> getDatabases(); + + /** + * Asynchronous version of {@link ArangoDB#getAccessibleDatabases()} + */ + CompletableFuture> getAccessibleDatabases(); + + /** + * Asynchronous version of {@link ArangoDB#getAccessibleDatabasesFor(String)} + */ + CompletableFuture> getAccessibleDatabasesFor(String user); + + /** + * Asynchronous version of {@link ArangoDB#getVersion()} + */ + CompletableFuture getVersion(); + + /** + * Asynchronous version of {@link ArangoDB#getEngine()} + */ + CompletableFuture getEngine(); + + /** + * Asynchronous version of {@link ArangoDB#getRole()} + */ + CompletableFuture getRole(); + + /** + * Asynchronous version of {@link ArangoDB#getServerId()} + */ + CompletableFuture getServerId(); + + /** + * Asynchronous version of {@link ArangoDB#createUser(String, String)} + */ + CompletableFuture createUser(String user, String passwd); + + /** + * Asynchronous version of {@link ArangoDB#createUser(String, String, UserCreateOptions)} + */ + CompletableFuture createUser(String user, String passwd, UserCreateOptions options); + + /** + * Asynchronous version of {@link ArangoDB#deleteUser(String)} + */ + CompletableFuture deleteUser(String user); + + /** + * Asynchronous version of {@link ArangoDB#getUser(String)} + */ + CompletableFuture getUser(String user); + + /** + * Asynchronous version of {@link ArangoDB#getUsers()} + */ + CompletableFuture> getUsers(); + + /** + * Asynchronous version of {@link ArangoDB#updateUser(String, UserUpdateOptions)} + */ + CompletableFuture updateUser(String user, UserUpdateOptions options); + + /** + * Asynchronous version of {@link ArangoDB#replaceUser(String, UserUpdateOptions)} + */ + CompletableFuture replaceUser(String user, UserUpdateOptions options); + + /** + * Asynchronous version of {@link ArangoDB#grantDefaultDatabaseAccess(String, Permissions)} + */ + CompletableFuture grantDefaultDatabaseAccess(String user, Permissions permissions); + + /** + * Asynchronous version of {@link ArangoDB#grantDefaultCollectionAccess(String, Permissions)} + */ + CompletableFuture grantDefaultCollectionAccess(String user, Permissions permissions); + + /** + * Asynchronous version of {@link ArangoDB#execute(Request, Class)} + */ + CompletableFuture> execute(Request request, Class type); + + /** + * Asynchronous version of {@link ArangoDB#getLogEntries(LogOptions)} + */ + CompletableFuture getLogEntries(LogOptions options); + + /** + * Asynchronous version of {@link ArangoDB#getLogLevel()} + */ + CompletableFuture getLogLevel(); + + /** + * Asynchronous version of {@link ArangoDB#getLogLevel(LogLevelOptions)} + */ + CompletableFuture getLogLevel(LogLevelOptions options); + + /** + * Asynchronous version of {@link ArangoDB#setLogLevel(LogLevelEntity)} + */ + CompletableFuture setLogLevel(LogLevelEntity entity); + + /** + * Asynchronous version of {@link ArangoDB#setLogLevel(LogLevelEntity, LogLevelOptions)} + */ + CompletableFuture setLogLevel(LogLevelEntity entity, LogLevelOptions options); + + /** + * Asynchronous version of {@link ArangoDB#getQueryOptimizerRules()} + */ + CompletableFuture> getQueryOptimizerRules(); + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java deleted file mode 100644 index 8c0c081fa..000000000 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java +++ /dev/null @@ -1,502 +0,0 @@ -///* -// * DISCLAIMER -// * -// * Copyright 2018 ArangoDB GmbH, Cologne, Germany -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// * -// * Copyright holder is ArangoDB GmbH, Cologne, Germany -// */ -// -//package com.arangodb.internal; -// -//import com.arangodb.ArangoCollection; -//import com.arangodb.ArangoCollectionAsync; -//import com.arangodb.ArangoDBException; -//import com.arangodb.entity.*; -//import com.arangodb.internal.util.DocumentUtil; -//import com.arangodb.model.*; -//import com.arangodb.util.RawData; -//import org.slf4j.Logger; -//import org.slf4j.LoggerFactory; -// -//import java.util.Collection; -// -//import static com.arangodb.internal.serde.SerdeUtils.constructParametricType; -// -///** -// * @author Mark Vollmary -// * @author Michele Rastelli -// */ -//public class ArangoCollectionAsyncImpl extends InternalArangoCollection implements ArangoCollectionAsync { -// -// private static final Logger LOGGER = LoggerFactory.getLogger(ArangoCollectionAsyncImpl.class); -// -// protected ArangoCollectionAsyncImpl(final ArangoDatabaseImpl db, final String name) { -// super(db, name); -// } -// -// @Override -// public DocumentCreateEntity insertDocument(final Object value) { -// return executor.execute(insertDocumentRequest(value, new DocumentCreateOptions()), -// constructParametricType(DocumentCreateEntity.class, Void.class)); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public DocumentCreateEntity insertDocument(final T value, final DocumentCreateOptions options) { -// return insertDocument(value, options, (Class) value.getClass()); -// } -// -// @Override -// public DocumentCreateEntity insertDocument(final T value, final DocumentCreateOptions options, -// final Class type) { -// return executor.execute(insertDocumentRequest(value, options), -// constructParametricType(DocumentCreateEntity.class, type)); -// } -// -// @Override -// public MultiDocumentEntity> insertDocuments(RawData values) { -// return executor -// .execute(insertDocumentsRequest(values, new DocumentCreateOptions()), -// insertDocumentsResponseDeserializer(Void.class)); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public MultiDocumentEntity> insertDocuments(RawData values, -// DocumentCreateOptions options) { -// return executor -// .execute(insertDocumentsRequest(values, options), -// insertDocumentsResponseDeserializer((Class) values.getClass())); -// } -// -// @Override -// public MultiDocumentEntity> insertDocuments(final Iterable values) { -// return insertDocuments(values, new DocumentCreateOptions()); -// } -// -// @Override -// public MultiDocumentEntity> insertDocuments( -// final Iterable values, final DocumentCreateOptions options) { -// return executor -// .execute(insertDocumentsRequest(values, options), -// insertDocumentsResponseDeserializer(Void.class)); -// } -// -// @Override -// public MultiDocumentEntity> insertDocuments(Iterable values, -// DocumentCreateOptions options, -// Class type) { -// return executor -// .execute(insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer(type)); -// } -// -// @Override -// public DocumentImportEntity importDocuments(final Iterable values) { -// return importDocuments(values, new DocumentImportOptions()); -// } -// -// @Override -// public DocumentImportEntity importDocuments(final Iterable values, final DocumentImportOptions options) { -// return executor.execute(importDocumentsRequest(values, options), DocumentImportEntity.class); -// } -// -// @Override -// public DocumentImportEntity importDocuments(RawData values) { -// return importDocuments(values, new DocumentImportOptions()); -// } -// -// @Override -// public DocumentImportEntity importDocuments(RawData values, DocumentImportOptions options) { -// return executor.execute(importDocumentsRequest(values, options), DocumentImportEntity.class); -// } -// -// @Override -// public T getDocument(final String key, final Class type) { -// return getDocument(key, type, new DocumentReadOptions()); -// } -// -// @Override -// public T getDocument(final String key, final Class type, final DocumentReadOptions options) { -// DocumentUtil.validateDocumentKey(key); -// try { -// return executor.execute(getDocumentRequest(key, options), getDocumentResponseDeserializer(type)); -// } catch (final ArangoDBException e) { -// if (LOGGER.isDebugEnabled()) { -// LOGGER.debug(e.getMessage(), e); -// } -// -// // handle Response: 404, Error: 1655 - transaction not found -// if (e.getErrorNum() != null && e.getErrorNum() == 1655) { -// throw e; -// } -// -// if ((e.getResponseCode() != null && (e.getResponseCode() == 404 || e.getResponseCode() == 304 -// || e.getResponseCode() == 412))) { -// return null; -// } -// throw e; -// } -// } -// -// @Override -// public MultiDocumentEntity getDocuments(final Iterable keys, final Class type) { -// return getDocuments(keys, type, new DocumentReadOptions()); -// } -// -// @Override -// public MultiDocumentEntity getDocuments( -// final Iterable keys, final Class type, final DocumentReadOptions options) { -// return executor.execute(getDocumentsRequest(keys, options), getDocumentsResponseDeserializer(type)); -// } -// -// @Override -// public DocumentUpdateEntity replaceDocument(final String key, final Object value) { -// return executor.execute(replaceDocumentRequest(key, value, new DocumentReplaceOptions()), -// constructParametricType(DocumentUpdateEntity.class, Void.class)); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public DocumentUpdateEntity replaceDocument( -// final String key, final T value, final DocumentReplaceOptions options) { -// return replaceDocument(key, value, options, (Class) value.getClass()); -// } -// -// @Override -// public DocumentUpdateEntity replaceDocument(String key, T value, DocumentReplaceOptions options, -// Class type) { -// return executor.execute(replaceDocumentRequest(key, value, options), -// constructParametricType(DocumentUpdateEntity.class, type)); -// } -// -// @Override -// public MultiDocumentEntity> replaceDocuments(RawData values) { -// return executor.execute(replaceDocumentsRequest(values, new DocumentReplaceOptions()), -// replaceDocumentsResponseDeserializer(Void.class)); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public MultiDocumentEntity> replaceDocuments(RawData values, -// DocumentReplaceOptions options) { -// return executor.execute(replaceDocumentsRequest(values, options), -// replaceDocumentsResponseDeserializer((Class) values.getClass())); -// } -// -// @Override -// public MultiDocumentEntity> replaceDocuments(final Iterable values) { -// return replaceDocuments(values, new DocumentReplaceOptions()); -// } -// -// @Override -// public MultiDocumentEntity> replaceDocuments( -// final Iterable values, final DocumentReplaceOptions options) { -// return executor.execute(replaceDocumentsRequest(values, options), -// replaceDocumentsResponseDeserializer(Void.class)); -// } -// -// @Override -// public MultiDocumentEntity> replaceDocuments(Iterable values, -// DocumentReplaceOptions options, -// Class type) { -// return executor.execute(replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(type)); -// } -// -// @Override -// public DocumentUpdateEntity updateDocument(final String key, final Object value) { -// return updateDocument(key, value, new DocumentUpdateOptions(), Void.class); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public DocumentUpdateEntity updateDocument( -// final String key, final T value, final DocumentUpdateOptions options) { -// return updateDocument(key, value, options, (Class) value.getClass()); -// } -// -// @Override -// public DocumentUpdateEntity updateDocument( -// final String key, final Object value, final DocumentUpdateOptions options, final Class returnType) { -// return executor.execute(updateDocumentRequest(key, value, options), -// constructParametricType(DocumentUpdateEntity.class, returnType)); -// } -// -// @Override -// public MultiDocumentEntity> updateDocuments(RawData values) { -// return executor -// .execute(updateDocumentsRequest(values, new DocumentUpdateOptions()), -// updateDocumentsResponseDeserializer(Void.class)); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public MultiDocumentEntity> updateDocuments(RawData values, -// DocumentUpdateOptions options) { -// return executor -// .execute(updateDocumentsRequest(values, options), -// updateDocumentsResponseDeserializer((Class) values.getClass())); -// } -// -// @Override -// public MultiDocumentEntity> updateDocuments(final Iterable values) { -// return updateDocuments(values, new DocumentUpdateOptions()); -// } -// -// @Override -// public MultiDocumentEntity> updateDocuments( -// final Iterable values, final DocumentUpdateOptions options) { -// return updateDocuments(values, options, Void.class); -// } -// -// @Override -// public MultiDocumentEntity> updateDocuments( -// final Iterable values, final DocumentUpdateOptions options, final Class returnType) { -// return executor -// .execute(updateDocumentsRequest(values, options), updateDocumentsResponseDeserializer(returnType)); -// } -// -// @Override -// public DocumentDeleteEntity deleteDocument(final String key) { -// return deleteDocument(key, new DocumentDeleteOptions()); -// } -// -// @Override -// public DocumentDeleteEntity deleteDocument(String key, DocumentDeleteOptions options) { -// return deleteDocument(key, options, Void.class); -// } -// -// @Override -// public DocumentDeleteEntity deleteDocument( -// final String key, final DocumentDeleteOptions options, final Class type) { -// return executor.execute(deleteDocumentRequest(key, options), -// constructParametricType(DocumentDeleteEntity.class, type)); -// } -// -// @Override -// public MultiDocumentEntity> deleteDocuments(RawData values) { -// return executor.execute(deleteDocumentsRequest(values, new DocumentDeleteOptions()), -// deleteDocumentsResponseDeserializer(Void.class)); -// } -// -// @Override -// @SuppressWarnings("unchecked") -// public MultiDocumentEntity> deleteDocuments(RawData values, -// DocumentDeleteOptions options) { -// return executor.execute(deleteDocumentsRequest(values, options), -// deleteDocumentsResponseDeserializer((Class) values.getClass())); -// } -// -// @Override -// public MultiDocumentEntity> deleteDocuments(final Iterable values) { -// return deleteDocuments(values, new DocumentDeleteOptions()); -// } -// -// @Override -// public MultiDocumentEntity> deleteDocuments( -// final Iterable values, final DocumentDeleteOptions options) { -// return deleteDocuments(values, options, Void.class); -// } -// -// @Override -// public MultiDocumentEntity> deleteDocuments( -// final Iterable values, final DocumentDeleteOptions options, final Class type) { -// return executor.execute(deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer(type)); -// } -// -// @Override -// public Boolean documentExists(final String key) { -// return documentExists(key, new DocumentExistsOptions()); -// } -// -// @Override -// public Boolean documentExists(final String key, final DocumentExistsOptions options) { -// try { -// executor.execute(documentExistsRequest(key, options), Void.class); -// return true; -// } catch (final ArangoDBException e) { -// -// // handle Response: 404, Error: 1655 - transaction not found -// if (e.getErrorNum() != null && e.getErrorNum() == 1655) { -// throw e; -// } -// -// if ((e.getResponseCode() != null && -// (e.getResponseCode() == 404 || e.getResponseCode() == 304 || e.getResponseCode() == 412))) { -// return false; -// } -// throw e; -// } -// } -// -// @Override -// public IndexEntity getIndex(final String id) { -// return executor.execute(getIndexRequest(id), IndexEntity.class); -// } -// -// @Override -// public InvertedIndexEntity getInvertedIndex(String id) { -// return executor.execute(getIndexRequest(id), InvertedIndexEntity.class); -// } -// -// @Override -// public String deleteIndex(final String id) { -// return executor.execute(deleteIndexRequest(id), deleteIndexResponseDeserializer()); -// } -// -// @Override -// public IndexEntity ensurePersistentIndex(final Iterable fields, final PersistentIndexOptions options) { -// return executor.execute(createPersistentIndexRequest(fields, options), IndexEntity.class); -// } -// -// @Override -// public InvertedIndexEntity ensureInvertedIndex(final InvertedIndexOptions options) { -// return executor.execute(createInvertedIndexRequest(options), InvertedIndexEntity.class); -// } -// -// @Override -// public IndexEntity ensureGeoIndex(final Iterable fields, final GeoIndexOptions options) { -// return executor.execute(createGeoIndexRequest(fields, options), IndexEntity.class); -// } -// -// @Deprecated -// @Override -// public IndexEntity ensureFulltextIndex(final Iterable fields, final FulltextIndexOptions options) { -// return executor.execute(createFulltextIndexRequest(fields, options), IndexEntity.class); -// } -// -// @Override -// public IndexEntity ensureTtlIndex(final Iterable fields, final TtlIndexOptions options) { -// return executor.execute(createTtlIndexRequest(fields, options), IndexEntity.class); -// } -// -// @Override -// public IndexEntity ensureZKDIndex(final Iterable fields, final ZKDIndexOptions options) { -// return executor.execute(createZKDIndexRequest(fields, options), IndexEntity.class); -// } -// -// @Override -// public Collection getIndexes() { -// return executor.execute(getIndexesRequest(), getIndexesResponseDeserializer()); -// } -// -// @Override -// public Collection getInvertedIndexes() { -// return executor.execute(getIndexesRequest(), getInvertedIndexesResponseDeserializer()); -// } -// -// @Override -// public boolean exists() { -// try { -// getInfo(); -// return true; -// } catch (final ArangoDBException e) { -// if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(e.getErrorNum())) { -// return false; -// } -// throw e; -// } -// } -// -// @Override -// public CollectionEntity truncate() { -// return truncate(null); -// } -// -// @Override -// public CollectionEntity truncate(CollectionTruncateOptions options) { -// return executor.execute(truncateRequest(options), CollectionEntity.class); -// } -// -// @Override -// public CollectionPropertiesEntity count() { -// return count(null); -// } -// -// @Override -// public CollectionPropertiesEntity count(CollectionCountOptions options) { -// return executor.execute(countRequest(options), CollectionPropertiesEntity.class); -// } -// -// @Override -// public CollectionEntity create() { -// return db().createCollection(name()); -// } -// -// @Override -// public CollectionEntity create(final CollectionCreateOptions options) { -// return db().createCollection(name(), options); -// } -// -// @Override -// public void drop() { -// executor.execute(dropRequest(null), Void.class); -// } -// -// @Override -// public void drop(final boolean isSystem) { -// executor.execute(dropRequest(isSystem), Void.class); -// } -// -// @Override -// public CollectionEntity getInfo() { -// return executor.execute(getInfoRequest(), CollectionEntity.class); -// } -// -// @Override -// public CollectionPropertiesEntity getProperties() { -// return executor.execute(getPropertiesRequest(), CollectionPropertiesEntity.class); -// } -// -// @Override -// public CollectionPropertiesEntity changeProperties(final CollectionPropertiesOptions options) { -// return executor.execute(changePropertiesRequest(options), CollectionPropertiesEntity.class); -// } -// -// @Override -// public CollectionEntity rename(final String newName) { -// return executor.execute(renameRequest(newName), CollectionEntity.class); -// } -// -// @Override -// public ShardEntity getResponsibleShard(final Object value) { -// return executor.execute(responsibleShardRequest(value), ShardEntity.class); -// } -// -// @Override -// public CollectionRevisionEntity getRevision() { -// return executor.execute(getRevisionRequest(), CollectionRevisionEntity.class); -// } -// -// @Override -// public void grantAccess(final String user, final Permissions permissions) { -// executor.execute(grantAccessRequest(user, permissions), Void.class); -// } -// -// @Override -// public void revokeAccess(final String user) { -// executor.execute(grantAccessRequest(user, Permissions.NONE), Void.class); -// } -// -// @Override -// public void resetAccess(final String user) { -// executor.execute(resetAccessRequest(user), Void.class); -// } -// -// @Override -// public Permissions getPermissions(final String user) { -// return executor.execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); -// } -// -//} From 95179ec35e47b1d2cebb3bbf6bf53fed2c41a53c Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 11 Oct 2023 13:52:12 +0200 Subject: [PATCH 15/62] ArangoDBAsync --- core/src/main/java/com/arangodb/ArangoDB.java | 5 + .../main/java/com/arangodb/ArangoDBAsync.java | 29 +- .../arangodb/internal/ArangoDBAsyncImpl.java | 362 +++++----- .../com/arangodb/internal/ArangoDBImpl.java | 5 + .../arangodb/internal/InternalArangoDB.java | 4 + .../java/com/arangodb/ArangoDBAsyncTest.java | 659 ++++++++++++++++++ 6 files changed, 864 insertions(+), 200 deletions(-) create mode 100644 driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java diff --git a/core/src/main/java/com/arangodb/ArangoDB.java b/core/src/main/java/com/arangodb/ArangoDB.java index bf9881cb8..47535af5d 100644 --- a/core/src/main/java/com/arangodb/ArangoDB.java +++ b/core/src/main/java/com/arangodb/ArangoDB.java @@ -47,6 +47,11 @@ @ThreadSafe public interface ArangoDB extends ArangoSerdeAccessor { + /** + * @return the asynchronous version of this class + */ + ArangoDBAsync async(); + /** * Releases all connections to the server and clear the connection pool. */ diff --git a/core/src/main/java/com/arangodb/ArangoDBAsync.java b/core/src/main/java/com/arangodb/ArangoDBAsync.java index 8ce4ef2a1..282a1cfbb 100644 --- a/core/src/main/java/com/arangodb/ArangoDBAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDBAsync.java @@ -33,6 +33,11 @@ @ThreadSafe public interface ArangoDBAsync extends ArangoSerdeAccessor { + /** + * @return the synchronous version of this class + */ + ArangoDB sync(); + // /** // * Returns a {@code ArangoDatabase} instance for the {@code _system} database. // * @@ -68,25 +73,25 @@ public interface ArangoDBAsync extends ArangoSerdeAccessor { */ CompletableFuture> getDatabases(); - /** - * Asynchronous version of {@link ArangoDB#getAccessibleDatabases()} - */ - CompletableFuture> getAccessibleDatabases(); +// /** +// * Asynchronous version of {@link ArangoDB#getAccessibleDatabases()} +// */ +// CompletableFuture> getAccessibleDatabases(); /** * Asynchronous version of {@link ArangoDB#getAccessibleDatabasesFor(String)} */ CompletableFuture> getAccessibleDatabasesFor(String user); - /** - * Asynchronous version of {@link ArangoDB#getVersion()} - */ - CompletableFuture getVersion(); +// /** +// * Asynchronous version of {@link ArangoDB#getVersion()} +// */ +// CompletableFuture getVersion(); - /** - * Asynchronous version of {@link ArangoDB#getEngine()} - */ - CompletableFuture getEngine(); +// /** +// * Asynchronous version of {@link ArangoDB#getEngine()} +// */ +// CompletableFuture getEngine(); /** * Asynchronous version of {@link ArangoDB#getRole()} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java index db4511251..25f045615 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java @@ -1,68 +1,49 @@ -///* -// * DISCLAIMER -// * -// * Copyright 2016 ArangoDB GmbH, Cologne, Germany -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// * -// * Copyright holder is ArangoDB GmbH, Cologne, Germany -// */ -// -//package com.arangodb.internal; -// -//import com.arangodb.*; -//import com.arangodb.entity.*; -//import com.arangodb.internal.config.ArangoConfig; -//import com.arangodb.internal.net.HostHandler; -//import com.arangodb.internal.net.HostResolver; -//import com.arangodb.internal.net.ProtocolProvider; -//import com.arangodb.internal.serde.SerdeUtils; -//import com.arangodb.model.*; -//import org.slf4j.Logger; -//import org.slf4j.LoggerFactory; -// -//import java.util.Collection; -// -///** -// * @author Mark Vollmary -// * @author Heiko Kernbach -// * @author Michele Rastelli -// */ -//public class ArangoDBAsyncImpl extends InternalArangoDB implements ArangoDB { -// -// private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBAsyncImpl.class); -// private final HostHandler hostHandler; -// -// public ArangoDBAsyncImpl(final ArangoConfig config, -// final HostResolver hostResolver, final ProtocolProvider protocolProvider, -// final HostHandler hostHandler) { -// super(new ArangoExecutorSync(protocolProvider.createProtocol(config, hostHandler), config), config.getInternalSerde()); -// this.hostHandler = hostHandler; -// hostResolver.init(this.executorSync(), getSerde()); -// LOGGER.debug("ArangoDB Client is ready to use"); -// } -// -// @Override -// public void shutdown() { -// executorSync.disconnect(); -// } -// -// @Override -// public void updateJwt(String jwt) { -// hostHandler.setJwt(jwt); -// executorSync.setJwt(jwt); -// } -// +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.ArangoDB; +import com.arangodb.ArangoDBAsync; +import com.arangodb.Request; +import com.arangodb.Response; +import com.arangodb.entity.*; +import com.arangodb.internal.serde.SerdeUtils; +import com.arangodb.model.*; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +/** + * @author Mark Vollmary + * @author Heiko Kernbach + * @author Michele Rastelli + */ +public class ArangoDBAsyncImpl extends InternalArangoDB implements ArangoDBAsync { + + private final ArangoDB arangoDB; + public ArangoDBAsyncImpl(final ArangoDBImpl arangoDB) { + super(arangoDB); + this.arangoDB = arangoDB; + } + // @Override // public ArangoDatabase db() { // return db(ArangoRequestParam.SYSTEM); @@ -75,134 +56,139 @@ // // @Override // public ArangoMetrics metrics() { -// return new ArangoMetricsImpl(executorSync.getQueueTimeMetrics()); -// } -// -// @Override -// public Boolean createDatabase(final String dbName) { -// return createDatabase(new DBCreateOptions().name(dbName)); -// } -// -// @Override -// public Boolean createDatabase(DBCreateOptions options) { -// return executorSync.execute(createDatabaseRequest(options), createDatabaseResponseDeserializer()); -// } -// -// @Override -// public Collection getDatabases() { -// return executorSync.execute(getDatabasesRequest(db().name()), getDatabaseResponseDeserializer()); -// } -// -// @Override -// public Collection getAccessibleDatabases() { +// return new ArangoMetricsImpl(executorAsync().getQueueTimeMetrics()); +// } + + @Override + public ArangoDB sync() { + return arangoDB; + } + + @Override + public CompletableFuture createDatabase(final String dbName) { + return createDatabase(new DBCreateOptions().name(dbName)); + } + + @Override + public CompletableFuture createDatabase(DBCreateOptions options) { + return executorAsync().execute(createDatabaseRequest(options), createDatabaseResponseDeserializer()); + } + + @Override + public CompletableFuture> getDatabases() { + return executorAsync().execute(getDatabasesRequest(ArangoRequestParam.SYSTEM), getDatabaseResponseDeserializer()); + } + +// @Override +// public CompletableFuture> getAccessibleDatabases() { // return db().getAccessibleDatabases(); // } -// + + @Override + public CompletableFuture> getAccessibleDatabasesFor(final String user) { + return executorAsync().execute(getAccessibleDatabasesForRequest(ArangoRequestParam.SYSTEM, user), + getAccessibleDatabasesForResponseDeserializer()); + } + // @Override -// public Collection getAccessibleDatabasesFor(final String user) { -// return executorSync.execute(getAccessibleDatabasesForRequest(db().name(), user), -// getAccessibleDatabasesForResponseDeserializer()); -// } -// -// @Override -// public ArangoDBVersion getVersion() { +// public CompletableFuture getVersion() { // return db().getVersion(); // } -// + // @Override -// public ArangoDBEngine getEngine() { +// public CompletableFuture getEngine() { // return db().getEngine(); // } -// -// @Override -// public ServerRole getRole() { -// return executorSync.execute(getRoleRequest(), getRoleResponseDeserializer()); -// } -// -// @Override -// public String getServerId() { -// return executorSync.execute(getServerIdRequest(), getServerIdResponseDeserializer()); -// } -// -// @Override -// public UserEntity createUser(final String user, final String passwd) { -// return executorSync.execute(createUserRequest(db().name(), user, passwd, new UserCreateOptions()), -// UserEntity.class); -// } -// -// @Override -// public UserEntity createUser(final String user, final String passwd, final UserCreateOptions options) { -// return executorSync.execute(createUserRequest(db().name(), user, passwd, options), UserEntity.class); -// } -// -// @Override -// public void deleteUser(final String user) { -// executorSync.execute(deleteUserRequest(db().name(), user), Void.class); -// } -// -// @Override -// public UserEntity getUser(final String user) { -// return executorSync.execute(getUserRequest(db().name(), user), UserEntity.class); -// } -// -// @Override -// public Collection getUsers() { -// return executorSync.execute(getUsersRequest(db().name()), getUsersResponseDeserializer()); -// } -// -// @Override -// public UserEntity updateUser(final String user, final UserUpdateOptions options) { -// return executorSync.execute(updateUserRequest(db().name(), user, options), UserEntity.class); -// } -// -// @Override -// public UserEntity replaceUser(final String user, final UserUpdateOptions options) { -// return executorSync.execute(replaceUserRequest(db().name(), user, options), UserEntity.class); -// } -// -// @Override -// public void grantDefaultDatabaseAccess(final String user, final Permissions permissions) { -// executorSync.execute(updateUserDefaultDatabaseAccessRequest(user, permissions), Void.class); -// } -// -// @Override -// public void grantDefaultCollectionAccess(final String user, final Permissions permissions) { -// executorSync.execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); -// } -// -// @Override -// public Response execute(Request request, Class type) { -// return executorSync.execute(executeRequest(request), responseDeserializer(type)); -// } -// -// @Override -// public LogEntriesEntity getLogEntries(final LogOptions options) { -// return executorSync.execute(getLogEntriesRequest(options), LogEntriesEntity.class); -// } -// -// @Override -// public LogLevelEntity getLogLevel() { -// return getLogLevel(new LogLevelOptions()); -// } -// -// @Override -// public LogLevelEntity getLogLevel(final LogLevelOptions options) { -// return executorSync.execute(getLogLevelRequest(options), LogLevelEntity.class); -// } -// -// @Override -// public LogLevelEntity setLogLevel(final LogLevelEntity entity) { -// return setLogLevel(entity, new LogLevelOptions()); -// } -// -// @Override -// public LogLevelEntity setLogLevel(final LogLevelEntity entity, final LogLevelOptions options) { -// return executorSync.execute(setLogLevelRequest(entity, options), LogLevelEntity.class); -// } -// -// @Override -// public Collection getQueryOptimizerRules() { -// return executorSync.execute(getQueryOptimizerRulesRequest(), SerdeUtils.constructListType(QueryOptimizerRule.class)); -// } -// -//} + + @Override + public CompletableFuture getRole() { + return executorAsync().execute(getRoleRequest(), getRoleResponseDeserializer()); + } + + @Override + public CompletableFuture getServerId() { + return executorAsync().execute(getServerIdRequest(), getServerIdResponseDeserializer()); + } + + @Override + public CompletableFuture createUser(final String user, final String passwd) { + return executorAsync().execute(createUserRequest(ArangoRequestParam.SYSTEM, user, passwd, new UserCreateOptions()), + UserEntity.class); + } + + @Override + public CompletableFuture createUser(final String user, final String passwd, final UserCreateOptions options) { + return executorAsync().execute(createUserRequest(ArangoRequestParam.SYSTEM, user, passwd, options), UserEntity.class); + } + + @Override + public CompletableFuture deleteUser(final String user) { + return executorAsync().execute(deleteUserRequest(ArangoRequestParam.SYSTEM, user), Void.class); + } + + @Override + public CompletableFuture getUser(final String user) { + return executorAsync().execute(getUserRequest(ArangoRequestParam.SYSTEM, user), UserEntity.class); + } + + @Override + public CompletableFuture> getUsers() { + return executorAsync().execute(getUsersRequest(ArangoRequestParam.SYSTEM), getUsersResponseDeserializer()); + } + + @Override + public CompletableFuture updateUser(final String user, final UserUpdateOptions options) { + return executorAsync().execute(updateUserRequest(ArangoRequestParam.SYSTEM, user, options), UserEntity.class); + } + + @Override + public CompletableFuture replaceUser(final String user, final UserUpdateOptions options) { + return executorAsync().execute(replaceUserRequest(ArangoRequestParam.SYSTEM, user, options), UserEntity.class); + } + + @Override + public CompletableFuture grantDefaultDatabaseAccess(final String user, final Permissions permissions) { + return executorAsync().execute(updateUserDefaultDatabaseAccessRequest(user, permissions), Void.class); + } + + @Override + public CompletableFuture grantDefaultCollectionAccess(final String user, final Permissions permissions) { + return executorAsync().execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); + } + + @Override + public CompletableFuture> execute(Request request, Class type) { + return executorAsync().execute(executeRequest(request), responseDeserializer(type)); + } + + @Override + public CompletableFuture getLogEntries(final LogOptions options) { + return executorAsync().execute(getLogEntriesRequest(options), LogEntriesEntity.class); + } + + @Override + public CompletableFuture getLogLevel() { + return getLogLevel(new LogLevelOptions()); + } + + @Override + public CompletableFuture getLogLevel(final LogLevelOptions options) { + return executorAsync().execute(getLogLevelRequest(options), LogLevelEntity.class); + } + + @Override + public CompletableFuture setLogLevel(final LogLevelEntity entity) { + return setLogLevel(entity, new LogLevelOptions()); + } + + @Override + public CompletableFuture setLogLevel(final LogLevelEntity entity, final LogLevelOptions options) { + return executorAsync().execute(setLogLevelRequest(entity, options), LogLevelEntity.class); + } + + @Override + public CompletableFuture> getQueryOptimizerRules() { + return executorAsync().execute(getQueryOptimizerRulesRequest(), SerdeUtils.constructListType(QueryOptimizerRule.class)); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java index bfbcb7f54..ad3321c24 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java @@ -52,6 +52,11 @@ public ArangoDBImpl(final ArangoConfig config, LOGGER.debug("ArangoDB Client is ready to use"); } + @Override + public ArangoDBAsync async() { + return new ArangoDBAsyncImpl(this); + } + @Override public void shutdown() { executorSync().disconnect(); diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java index 372c4f988..65725511c 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java @@ -54,6 +54,10 @@ protected InternalArangoDB(final CommunicationProtocol protocol, final ArangoCon super(protocol, config, util); } + protected InternalArangoDB(final ArangoExecuteable other) { + super(other); + } + protected InternalRequest getRoleRequest() { return request(ArangoRequestParam.SYSTEM, RequestType.GET, PATH_API_ROLE); } diff --git a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java new file mode 100644 index 000000000..da241a4c6 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java @@ -0,0 +1,659 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.internal.ArangoRequestParam; +import com.arangodb.internal.serde.SerdeUtils; +import com.arangodb.model.*; +import com.arangodb.model.LogOptions.SortOrder; +import com.arangodb.util.RawJson; +import com.arangodb.util.UnicodeUtils; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Mark Vollmary + * @author ReÅŸat SABIQ + * @author Michele Rastelli + */ +class ArangoDBAsyncTest extends BaseJunit5 { + + private static final String DB1 = "ArangoDBTest_db1"; + private static final String DB2 = "ArangoDBTest_db2"; + + private static final String ROOT = "root"; + private static final String PW = "machts der hund"; + + @BeforeAll + static void initDBs() { + initDB(DB1); + initDB(DB2); + } + + @AfterAll + static void shutdown() { + dropDB(DB1); + dropDB(DB2); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// void getVersion(ArangoDB arangoDB) { +// final ArangoDBVersion version = arangoDB.async().getVersion(); +// assertThat(version.getServer()).isNotNull(); +// assertThat(version.getVersion()).isNotNull(); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createAndDeleteDatabase(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + final String dbName = rndDbName(); + final Boolean resultCreate = arangoDB.async().createDatabase(dbName).get(); + assertThat(resultCreate).isTrue(); +// final Boolean resultDelete = arangoDB.async().db(dbName).drop(); +// assertThat(resultDelete).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createWithNotNormalizedName(ArangoDB arangoDB) { + assumeTrue(supportsExtendedDbNames()); + + final String dbName = "testDB-\u006E\u0303\u00f1"; + String normalized = UnicodeUtils.normalize(dbName); + arangoDB.async().createDatabase(normalized); +// arangoDB.async().db(normalized).drop(); + + Throwable thrown = catchThrowable(() -> arangoDB.async().createDatabase(dbName)); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .hasMessageContaining("normalized"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createDatabaseWithOptions(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + assumeTrue(isAtLeastVersion(3, 6)); + final String dbName = rndDbName(); + final Boolean resultCreate = arangoDB.async().createDatabase(new DBCreateOptions() + .name(dbName) + .options(new DatabaseOptions() + .writeConcern(2) + .replicationFactor(2) + .sharding("") + ) + ).get(); + assertThat(resultCreate).isTrue(); + +// DatabaseEntity info = arangoDB.async().db(dbName).getInfo(); +// assertThat(info.getReplicationFactor().get()).isEqualTo(2); +// assertThat(info.getWriteConcern()).isEqualTo(2); +// assertThat(info.getSharding()).isEmpty(); +// +// final Boolean resultDelete = arangoDB.async().db(dbName).drop(); +// assertThat(resultDelete).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createDatabaseWithOptionsSatellite(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 6)); + + final String dbName = rndDbName(); + final Boolean resultCreate = arangoDB.async().createDatabase(new DBCreateOptions() + .name(dbName) + .options(new DatabaseOptions() + .writeConcern(2) + .replicationFactor(ReplicationFactor.ofSatellite()) + .sharding("") + ) + ).get(); + assertThat(resultCreate).isTrue(); + +// DatabaseEntity info = arangoDB.async().db(dbName).getInfo(); +// assertThat(info.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); +// assertThat(info.getWriteConcern()).isEqualTo(2); +// assertThat(info.getSharding()).isEmpty(); +// +// final Boolean resultDelete = arangoDB.async().db(dbName).drop(); +// assertThat(resultDelete).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createDatabaseWithUsers(ArangoDB arangoDB) throws InterruptedException, ExecutionException { + final String dbName = rndDbName(); + final Map extra = Collections.singletonMap("key", "value"); + final Boolean resultCreate = arangoDB.async().createDatabase(new DBCreateOptions() + .name(dbName) + .users(Collections.singletonList(new DatabaseUsersOptions() + .active(true) + .username("testUser") + .passwd("testPasswd") + .extra(extra) + )) + ).get(); + assertThat(resultCreate).isTrue(); + +// DatabaseEntity info = arangoDB.async().db(dbName).getInfo(); +// assertThat(info.getName()).isEqualTo(dbName); + + Optional retrievedUserOptional = arangoDB.async().getUsers().get().stream() + .filter(it -> it.getUser().equals("testUser")) + .findFirst(); + assertThat(retrievedUserOptional).isPresent(); + + UserEntity retrievedUser = retrievedUserOptional.get(); + assertThat(retrievedUser.getActive()).isTrue(); + assertThat(retrievedUser.getExtra()).isEqualTo(extra); + + // needed for active-failover tests only + Thread.sleep(2_000); + + ArangoDB arangoDBTestUser = new ArangoDB.Builder() + .loadProperties(config) + .user("testUser") + .password("testPasswd") + .build(); + + // check if testUser has been created and can access the created db +// ArangoCollection collection = arangoDBTestUser.async().db(dbName).collection("col-" + UUID.randomUUID()); +// collection.create(); +// arangoDBTestUser.shutdown(); +// +// final Boolean resultDelete = arangoDB.async().db(dbName).drop(); +// assertThat(resultDelete).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getDatabases(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + Collection dbs = arangoDB.async().getDatabases().get(); + assertThat(dbs).contains("_system", DB1); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// void getAccessibleDatabases(ArangoDB arangoDB) { +// final Collection dbs = arangoDB.async().getAccessibleDatabases(); +// assertThat(dbs).contains("_system"); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getAccessibleDatabasesFor(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + final Collection dbs = arangoDB.async().getAccessibleDatabasesFor("root").get(); + assertThat(dbs).contains("_system"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createUser(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + String username = "user-" + UUID.randomUUID(); + final UserEntity result = arangoDB.async().createUser(username, PW, null).get(); + assertThat(result.getUser()).isEqualTo(username); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void deleteUser(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + arangoDB.async().createUser(username, PW, null); + arangoDB.async().deleteUser(username); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getUserRoot(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + final UserEntity user = arangoDB.async().getUser(ROOT).get(); + assertThat(user.getUser()).isEqualTo(ROOT); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getUser(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + String username = "user-" + UUID.randomUUID(); + arangoDB.async().createUser(username, PW, null).get(); + final UserEntity user = arangoDB.async().getUser(username).get(); + assertThat(user.getUser()).isEqualTo(username); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getUsersOnlyRoot(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + final Collection users = arangoDB.async().getUsers().get(); + assertThat(users).isNotEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getUsers(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + String username = "user-" + UUID.randomUUID(); + // Allow & account for pre-existing users other than ROOT: + final Collection initialUsers = arangoDB.async().getUsers().get(); + + arangoDB.async().createUser(username, PW, null).get(); + final Collection users = arangoDB.async().getUsers().get(); + assertThat(users).hasSize(initialUsers.size() + 1); + + final List expected = new ArrayList<>(users.size()); + // Add initial users, including root: + for (final UserEntity userEntity : initialUsers) { + expected.add(userEntity.getUser()); + } + // Add username: + expected.add(username); + + for (final UserEntity user : users) { + assertThat(user.getUser()).isIn(expected); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void updateUserNoOptions(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + arangoDB.async().createUser(username, PW, null); + arangoDB.async().updateUser(username, null); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void updateUser(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + String username = "user-" + UUID.randomUUID(); + final Map extra = new HashMap<>(); + extra.put("hund", false); + arangoDB.async().createUser(username, PW, new UserCreateOptions().extra(extra)); + extra.put("hund", true); + extra.put("mund", true); + final UserEntity user = arangoDB.async().updateUser(username, new UserUpdateOptions().extra(extra)).get(); + assertThat(user.getExtra()).hasSize(2); + assertThat(user.getExtra()).containsKey("hund"); + assertThat(Boolean.valueOf(String.valueOf(user.getExtra().get("hund")))).isTrue(); + final UserEntity user2 = arangoDB.async().getUser(username).get(); + assertThat(user2.getExtra()).hasSize(2); + assertThat(user2.getExtra()).containsKey("hund"); + assertThat(Boolean.valueOf(String.valueOf(user2.getExtra().get("hund")))).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void replaceUser(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + String username = "user-" + UUID.randomUUID(); + final Map extra = new HashMap<>(); + extra.put("hund", false); + arangoDB.async().createUser(username, PW, new UserCreateOptions().extra(extra)).get(); + extra.remove("hund"); + extra.put("mund", true); + final UserEntity user = arangoDB.async().replaceUser(username, new UserUpdateOptions().extra(extra)).get(); + assertThat(user.getExtra()).hasSize(1); + assertThat(user.getExtra()).containsKey("mund"); + assertThat(Boolean.valueOf(String.valueOf(user.getExtra().get("mund")))).isTrue(); + final UserEntity user2 = arangoDB.async().getUser(username).get(); + assertThat(user2.getExtra()).hasSize(1); + assertThat(user2.getExtra()).containsKey("mund"); + assertThat(Boolean.valueOf(String.valueOf(user2.getExtra().get("mund")))).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void updateUserDefaultDatabaseAccess(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + arangoDB.async().createUser(username, PW); + arangoDB.async().grantDefaultDatabaseAccess(username, Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void updateUserDefaultCollectionAccess(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + arangoDB.async().createUser(username, PW); + arangoDB.async().grantDefaultCollectionAccess(username, Permissions.RW); + } + +// @Test +// void authenticationFailPassword() { +// final ArangoDB arangoDB = new ArangoDB.Builder() +// .loadProperties(config) +// .password("no").jwt(null).build(); +// Throwable thrown = catchThrowable(() -> arangoDB.async().getVersion().get()); +// assertThat(thrown).isInstanceOf(ArangoDBException.class); +// assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(401); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// void authenticationFailUser() { +// final ArangoDB arangoDB = new ArangoDB.Builder() +// .loadProperties(config) +// .user("no").jwt(null).build(); +// Throwable thrown = catchThrowable(() -> arangoDB.async().getVersion().get()); +// assertThat(thrown).isInstanceOf(ArangoDBException.class); +// assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(401); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void executeGetVersion(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + Request request = Request.builder() + .db(ArangoRequestParam.SYSTEM) + .method(Request.Method.GET) + .path("/_api/version") + .queryParam("details", "true") + .build(); + final Response response = arangoDB.async().execute(request, RawJson.class).get(); + JsonNode body = SerdeUtils.INSTANCE.parseJson(response.getBody().get()); + assertThat(body.get("version").isTextual()).isTrue(); + assertThat(body.get("details").isObject()).isTrue(); + assertThat(response.getResponseCode()).isEqualTo(200); + if (isAtLeastVersion(3, 9)) { + String header = response.getHeaders().get("x-arango-queue-time-seconds"); + assertThat(header).isNotNull(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntries(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + assertThat(logs.getTotal()).isPositive(); + assertThat(logs.getMessages()).hasSize(logs.getTotal().intValue()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesUpto(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logsUpto = arangoDB.async().getLogEntries(new LogOptions().upto(LogLevel.WARNING)).get(); + assertThat(logsUpto.getMessages()) + .map(LogEntriesEntity.Message::getLevel) + .doesNotContain("INFO"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesLevel(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logsInfo = arangoDB.async().getLogEntries(new LogOptions().level(LogLevel.INFO)).get(); + assertThat(logsInfo.getMessages()) + .map(LogEntriesEntity.Message::getLevel) + .containsOnly("INFO"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesStart(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + final Long firstId = logs.getMessages().get(0).getId(); + final LogEntriesEntity logsStart = arangoDB.async().getLogEntries(new LogOptions().start(firstId + 1)).get(); + assertThat(logsStart.getMessages()) + .map(LogEntriesEntity.Message::getId) + .doesNotContain(firstId); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesSize(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + int count = logs.getMessages().size(); + assertThat(count).isPositive(); + final LogEntriesEntity logsSize = arangoDB.async().getLogEntries(new LogOptions().size(count - 1)).get(); + assertThat(logsSize.getMessages()).hasSize(count - 1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesOffset(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + assertThat(logs.getTotal()).isPositive(); + Long firstId = logs.getMessages().get(0).getId(); + final LogEntriesEntity logsOffset = arangoDB.async().getLogEntries(new LogOptions().offset(1)).get(); + assertThat(logsOffset.getMessages()) + .map(LogEntriesEntity.Message::getId) + .doesNotContain(firstId); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesSearch(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + final LogEntriesEntity logsSearch = arangoDB.async().getLogEntries(new LogOptions().search(TEST_DB)).get(); + assertThat(logs.getTotal()).isGreaterThan(logsSearch.getTotal()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesSortAsc(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.async().getLogEntries(new LogOptions().sort(SortOrder.asc)).get(); + long lastId = -1; + List ids = logs.getMessages().stream() + .map(LogEntriesEntity.Message::getId) + .collect(Collectors.toList()); + for (final Long id : ids) { + assertThat(id).isGreaterThan(lastId); + lastId = id; + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesSortDesc(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.async().getLogEntries(new LogOptions().sort(SortOrder.desc)).get(); + long lastId = Long.MAX_VALUE; + List ids = logs.getMessages().stream() + .map(LogEntriesEntity.Message::getId) + .collect(Collectors.toList()); + for (final Long id : ids) { + assertThat(lastId).isGreaterThan(id); + lastId = id; + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogLevel(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); // it fails in 3.6 active-failover (BTS-362) + final LogLevelEntity logLevel = arangoDB.async().getLogLevel().get(); + assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.INFO); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void setLogLevel(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); // it fails in 3.6 active-failover (BTS-362) + final LogLevelEntity entity = new LogLevelEntity(); + try { + entity.setAgency(LogLevelEntity.LogLevel.ERROR); + final LogLevelEntity logLevel = arangoDB.async().setLogLevel(entity).get(); + assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + } finally { + entity.setAgency(LogLevelEntity.LogLevel.INFO); + arangoDB.async().setLogLevel(entity); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void setAllLogLevel(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 9)); + final LogLevelEntity entity = new LogLevelEntity(); + try { + entity.setAll(LogLevelEntity.LogLevel.ERROR); + final LogLevelEntity logLevel = arangoDB.async().setLogLevel(entity).get(); + assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + assertThat(logLevel.getQueries()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + LogLevelEntity retrievedLevels = arangoDB.async().getLogLevel().get(); + assertThat(retrievedLevels.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + } finally { + entity.setAll(LogLevelEntity.LogLevel.INFO); + arangoDB.async().setLogLevel(entity); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void logLevelWithServerId(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + assumeTrue(isCluster()); + String serverId = arangoDB.async().getServerId().get(); + LogLevelOptions options = new LogLevelOptions().serverId(serverId); + final LogLevelEntity entity = new LogLevelEntity(); + try { + entity.setGraphs(LogLevelEntity.LogLevel.ERROR); + final LogLevelEntity logLevel = arangoDB.async().setLogLevel(entity, options).get(); + assertThat(logLevel.getGraphs()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + assertThat(arangoDB.async().getLogLevel(options).get().getGraphs()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + } finally { + entity.setGraphs(LogLevelEntity.LogLevel.INFO); + arangoDB.async().setLogLevel(entity); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getQueryOptimizerRules(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + final Collection rules = arangoDB.async().getQueryOptimizerRules().get(); + assertThat(rules).isNotEmpty(); + for (QueryOptimizerRule rule : rules) { + assertThat(rule).isNotNull(); + assertThat(rule.getName()).isNotNull(); + QueryOptimizerRule.Flags flags = rule.getFlags(); + assertThat(flags.getHidden()).isNotNull(); + assertThat(flags.getClusterOnly()).isNotNull(); + assertThat(flags.getCanBeDisabled()).isNotNull(); + assertThat(flags.getCanCreateAdditionalPlans()).isNotNull(); + assertThat(flags.getDisabledByDefault()).isNotNull(); + assertThat(flags.getEnterpriseOnly()).isNotNull(); + } + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// void arangoDBException(ArangoDB arangoDB) { +// Throwable thrown = catchThrowable(() -> arangoDB.async().db("no").getInfo()); +// assertThat(thrown).isInstanceOf(ArangoDBException.class); +// ArangoDBException e = (ArangoDBException) thrown; +// assertThat(e.getResponseCode()).isEqualTo(404); +// assertThat(e.getErrorNum()).isEqualTo(1228); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// void fallbackHost() { +// final ArangoDB arangoDB = new ArangoDB.Builder() +// .loadProperties(config) +// .host("not-accessible", 8529).host("127.0.0.1", 8529).build(); +// final ArangoDBVersion version = arangoDB.async().getVersion(); +// assertThat(version).isNotNull(); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// void loadproperties() { +// Throwable thrown = catchThrowable(() -> new ArangoDB.Builder() +// .loadProperties(ConfigUtils.loadConfig("arangodb-bad.properties")) +// ); +// assertThat(thrown).isInstanceOf(IllegalArgumentException.class); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// void loadpropertiesWithPrefix() { +// ArangoDB adb = new arangoDB.async().Builder() +// .loadProperties(ConfigUtils.loadConfig("arangodb-with-prefix.properties", "adb")) +// .build(); +// adb.getVersion(); +// adb.shutdown(); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// void accessMultipleDatabases(ArangoDB arangoDB) { +// final ArangoDBVersion version1 = arangoDB.async().db(DB1).getVersion(); +// assertThat(version1).isNotNull(); +// final ArangoDBVersion version2 = arangoDB.async().db(DB2).getVersion(); +// assertThat(version2).isNotNull(); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("arangos") +// @Disabled("Manual execution only") +// void queueTime(ArangoDB arangoDB) throws InterruptedException, ExecutionException { +// List> futures = IntStream.range(0, 80) +// .mapToObj(i -> CompletableFuture.runAsync( +// () -> arangoDB.async().db().query("RETURN SLEEP(1)", Void.class), +// Executors.newFixedThreadPool(80)) +// ) +// .collect(Collectors.toList()); +// for (CompletableFuture f : futures) { +// f.get(); +// } +// +// QueueTimeMetrics qt = arangoDB.async().metrics().getQueueTime(); +// double avg = qt.getAvg(); +// QueueTimeSample[] values = qt.getValues(); +// if (isAtLeastVersion(3, 9)) { +// assertThat(values).hasSize(20); +// for (int i = 0; i < values.length; i++) { +// assertThat(values[i].value).isNotNegative(); +// if (i > 0) { +// assertThat(values[i].timestamp).isGreaterThanOrEqualTo(values[i - 1].timestamp); +// } +// } +// +// if (avg < 0.0) { +// System.err.println("avg < 0: " + avg); +// System.err.println("got values:"); +// for (QueueTimeSample v : values) { +// System.err.println(v.value); +// } +// } +// assertThat(avg).isNotNegative(); +// } else { +// assertThat(avg).isEqualTo(0.0); +// assertThat(values).isEmpty(); +// } +// +// } +} From 9676dfbe74e2422a3853af645c43c68ecf5e5bb6 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 11 Oct 2023 22:58:58 +0200 Subject: [PATCH 16/62] ArangoDatabaseAsync --- .../com/arangodb/ArangoCollectionAsync.java | 908 ++++++++++++++++++ .../main/java/com/arangodb/ArangoDBAsync.java | 12 +- .../com/arangodb/ArangoDatabaseAsync.java | 354 +++++++ .../arangodb/internal/ArangoDBAsyncImpl.java | 16 +- .../internal/ArangoDatabaseAsyncImpl.java | 433 +++++++++ .../arangodb/internal/ArangoDatabaseImpl.java | 8 + .../internal/InternalArangoDatabase.java | 14 +- 7 files changed, 1728 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/com/arangodb/ArangoCollectionAsync.java create mode 100644 core/src/main/java/com/arangodb/ArangoDatabaseAsync.java create mode 100644 core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java diff --git a/core/src/main/java/com/arangodb/ArangoCollectionAsync.java b/core/src/main/java/com/arangodb/ArangoCollectionAsync.java new file mode 100644 index 000000000..7549f531f --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoCollectionAsync.java @@ -0,0 +1,908 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.model.*; +import com.arangodb.util.RawData; + +import javax.annotation.concurrent.ThreadSafe; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +/** + * Interface for operations on ArangoDB collection level. + * + * @author Mark Vollmary + * @author Heiko Kernbach + * @author Michele Rastelli + * @see Documents API Documentation + */ +@ThreadSafe +public interface ArangoCollectionAsync extends ArangoSerdeAccessor { + +// FIXME: +// /** +// * The the handler of the database the collection is within +// * +// * @return database handler +// */ +// ArangoDatabaseAsync db(); + + /** + * The name of the collection + * + * @return collection name + */ + String name(); + + /** + * Creates a new document from the given document, unless there is already a document with the _key given. If no + * _key is given, a new unique _key is generated automatically. + * + * @param value A representation of a single document (POJO or {@link RawData} + * @return information about the document + * @see API + * Documentation + */ + CompletableFuture> insertDocument(Object value); + + /** + * Creates a new document from the given document, unless there is already a document with the _key given. If no + * _key is given, a new unique _key is generated automatically. + * + * @param value A representation of a single document (POJO or {@link RawData}) + * @param options Additional options + * @return information about the document + * @see API + * Documentation + */ + CompletableFuture> insertDocument(T value, DocumentCreateOptions options); + + /** + * Creates a new document from the given document, unless there is already a document with the _key given. If no + * _key is given, a new unique _key is generated automatically. + * + * @param value A representation of a single document (POJO or {@link RawData}) + * @param options Additional options + * @param type Deserialization target type for the returned documents. + * @return information about the document + * @see API + * Documentation + */ + CompletableFuture> insertDocument(T value, DocumentCreateOptions options, Class type); + + /** + * Creates new documents from the given documents, unless there is already a document with the _key given. If no + * _key is given, a new unique _key is generated automatically. + * + * @param values Raw data representing a collection of documents + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> insertDocuments(RawData values); + + /** + * Creates new documents from the given documents, unless there is already a document with the _key given. If no + * _key is given, a new unique _key is generated automatically. + * + * @param values Raw data representing a collection of documents + * @param options Additional options + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> insertDocuments( + RawData values, DocumentCreateOptions options); + + /** + * Creates new documents from the given documents, unless there is already a document with the _key given. If no + * _key is given, a new unique _key is generated automatically. + * + * @param values A List of documents + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> insertDocuments(Iterable values); + + /** + * Creates new documents from the given documents, unless there is already a document with the _key given. If no + * _key is given, a new unique _key is generated automatically. + * + * @param values A List of documents (POJO or {@link RawData}) + * @param options Additional options + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> insertDocuments( + Iterable values, DocumentCreateOptions options); + + /** + * Creates new documents from the given documents, unless there is already a document with the _key given. If no + * _key is given, a new unique _key is generated automatically. + * + * @param values A List of documents (POJO or {@link RawData}) + * @param options Additional options + * @param type Deserialization target type for the returned documents. + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> insertDocuments( + Iterable values, DocumentCreateOptions options, Class type); + + /** + * Bulk imports the given values into the collection. + * + * @param values A List of documents (POJO or {@link RawData}) + * @return information about the import + */ + CompletableFuture importDocuments(Iterable values); + + /** + * Bulk imports the given values into the collection. + * + * @param values A List of documents (POJO or {@link RawData}) + * @param options Additional options, can be null + * @return information about the import + */ + CompletableFuture importDocuments(Iterable values, DocumentImportOptions options); + + /** + * Bulk imports the given values into the collection. + * + * @param values Raw data representing a collection of documents + * @return information about the import + */ + CompletableFuture importDocuments(RawData values); + + /** + * Bulk imports the given values into the collection. + * + * @param values Raw data representing a collection of documents + * @param options Additional options, can be null + * @return information about the import + */ + CompletableFuture importDocuments(RawData values, DocumentImportOptions options); + + /** + * Retrieves the document with the given {@code key} from the collection. + * + * @param key The key of the document + * @param type The type of the document (POJO or {@link RawData}) + * @return the document identified by the key + * @see API + * Documentation + */ + CompletableFuture getDocument(String key, Class type); + + /** + * Retrieves the document with the given {@code key} from the collection. + * + * @param key The key of the document + * @param type The type of the document (POJO or {@link RawData}) + * @param options Additional options, can be null + * @return the document identified by the key + * @see API + * Documentation + */ + CompletableFuture getDocument(String key, Class type, DocumentReadOptions options); + + /** + * Retrieves multiple documents with the given {@code _key} from the collection. + * + * @param keys The keys of the documents + * @param type The type of the documents (POJO or {@link RawData}) + * @return the documents and possible errors + */ + CompletableFuture> getDocuments(Iterable keys, Class type); + + /** + * Retrieves multiple documents with the given {@code _key} from the collection. + * + * @param keys The keys of the documents + * @param type The type of the documents (POJO or {@link RawData}) + * @param options Additional options, can be null + * @return the documents and possible errors + */ + CompletableFuture> getDocuments(Iterable keys, Class type, DocumentReadOptions options); + + /** + * Replaces the document with {@code key} with the one in the body, provided there is such a document and no + * precondition is violated + * + * @param key The key of the document + * @param value A representation of a single document (POJO or {@link RawData}) + * @return information about the document + * @see + * API + * Documentation + */ + CompletableFuture> replaceDocument(String key, Object value); + + /** + * Replaces the document with {@code key} with the one in the body, provided there is such a document and no + * precondition is violated + * + * @param key The key of the document + * @param value A representation of a single document (POJO or {@link RawData}) + * @param options Additional options + * @return information about the document + * @see + * API + * Documentation + */ + CompletableFuture> replaceDocument(String key, T value, DocumentReplaceOptions options); + + /** + * Replaces the document with {@code key} with the one in the body, provided there is such a document and no + * precondition is violated + * + * @param key The key of the document + * @param value A representation of a single document (POJO or {@link RawData}) + * @param options Additional options + * @param type Deserialization target type for the returned documents. + * @return information about the document + * @see + * API + * Documentation + */ + CompletableFuture> replaceDocument(String key, T value, DocumentReplaceOptions options, Class type); + + /** + * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are + * specified by the _key attributes in the documents in values. + * + * @param values Raw data representing a collection of documents + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> replaceDocuments(RawData values); + + /** + * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are + * specified by the _key attributes in the documents in values. + * + * @param values Raw data representing a collection of documents + * @param options Additional options + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> replaceDocuments( + RawData values, DocumentReplaceOptions options); + + /** + * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are + * specified by the _key attributes in the documents in values. + * + * @param values A List of documents (POJO or {@link RawData}) + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> replaceDocuments(Iterable values); + + /** + * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are + * specified by the _key attributes in the documents in values. + * + * @param values A List of documents (POJO or {@link RawData}) + * @param options Additional options + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> replaceDocuments( + Iterable values, DocumentReplaceOptions options); + + /** + * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are + * specified by the _key attributes in the documents in values. + * + * @param values A List of documents (POJO or {@link RawData}) + * @param options Additional options + * @param type Deserialization target type for the returned documents. + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> replaceDocuments( + Iterable values, DocumentReplaceOptions options, Class type); + + /** + * Partially updates the document identified by document-key. The value must contain a document with the attributes + * to patch (the patch document). All attributes from the patch document will be added to the existing document if + * they do not yet exist, and overwritten in the existing document if they do exist there. + * + * @param key The key of the document + * @param value A representation of a single document (POJO or {@link RawData}) + * @return information about the document + * @see API + * Documentation + */ + CompletableFuture> updateDocument(String key, Object value); + + /** + * Partially updates the document identified by document-key. The value must contain a document with the attributes + * to patch (the patch document). All attributes from the patch document will be added to the existing document if + * they do not yet exist, and overwritten in the existing document if they do exist there. + * + * @param key The key of the document + * @param value A representation of a single document (POJO or {@link RawData}) + * @param options Additional options + * @return information about the document + * @see API + * Documentation + */ + CompletableFuture> updateDocument(String key, T value, DocumentUpdateOptions options); + + /** + * Partially updates the document identified by document-key. The value must contain a document with the attributes + * to patch (the patch document). All attributes from the patch document will be added to the existing document if + * they do not yet exist, and overwritten in the existing document if they do exist there. + * + * @param key The key of the document + * @param value A representation of a single document (POJO or {@link RawData}) + * @param options Additional options + * @param returnType Type of the returned newDocument and/or oldDocument + * @return information about the document + * @see API + * Documentation + */ + CompletableFuture> updateDocument(String key, Object value, DocumentUpdateOptions options, + Class returnType); + + /** + * Partially updates documents, the documents to update are specified by the _key attributes in the objects on + * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All + * attributes from the patch documents will be added to the existing documents if they do not yet exist, and + * overwritten in the existing documents if they do exist there. + * + * @param values Raw data representing a collection of documents + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> updateDocuments(RawData values); + + /** + * Partially updates documents, the documents to update are specified by the _key attributes in the objects on + * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All + * attributes from the patch documents will be added to the existing documents if they do not yet exist, and + * overwritten in the existing documents if they do exist there. + * + * @param values Raw data representing a collection of documents + * @param options Additional options + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> updateDocuments( + RawData values, DocumentUpdateOptions options); + + /** + * Partially updates documents, the documents to update are specified by the _key attributes in the objects on + * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All + * attributes from the patch documents will be added to the existing documents if they do not yet exist, and + * overwritten in the existing documents if they do exist there. + * + * @param values A list of documents (POJO or {@link RawData}) + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> updateDocuments(Iterable values); + + /** + * Partially updates documents, the documents to update are specified by the _key attributes in the objects on + * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All + * attributes from the patch documents will be added to the existing documents if they do not yet exist, and + * overwritten in the existing documents if they do exist there. + * + * @param values A list of documents (POJO or {@link RawData}) + * @param options Additional options + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> updateDocuments( + Iterable values, DocumentUpdateOptions options); + + /** + * Partially updates documents, the documents to update are specified by the _key attributes in the objects on + * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All + * attributes from the patch documents will be added to the existing documents if they do not yet exist, and + * overwritten in the existing documents if they do exist there. + * + * @param values A list of documents (POJO or {@link RawData}) + * @param options Additional options + * @param returnType Type of the returned newDocument and/or oldDocument + * @return information about the documents + * @see + * API + * Documentation + */ + CompletableFuture>> updateDocuments( + Iterable values, DocumentUpdateOptions options, Class returnType); + + /** + * Deletes the document with the given {@code key} from the collection. + * + * @param key The key of the document + * @return information about the document + * @see + * API + * Documentation + */ + CompletableFuture> deleteDocument(String key); + + /** + * Deletes the document with the given {@code key} from the collection. + * + * @param key The key of the document + * @param options Additional options + * @return information about the document + * @see + * API + * Documentation + */ + CompletableFuture> deleteDocument(String key, DocumentDeleteOptions options); + + /** + * Deletes the document with the given {@code key} from the collection. + * + * @param key The key of the document + * @param type Deserialization target type for the returned documents. + * @param options Additional options + * @return information about the document + * @see + * API + * Documentation + */ + CompletableFuture> deleteDocument(String key, DocumentDeleteOptions options, Class type); + + /** + * Deletes multiple documents from the collection. + * + * @param values Raw data representing the keys of the documents or the documents themselves + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> deleteDocuments(RawData values); + + /** + * Deletes multiple documents from the collection. + * + * @param values Raw data representing the keys of the documents or the documents themselves + * @param options Additional options + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> deleteDocuments( + RawData values, DocumentDeleteOptions options); + + /** + * Deletes multiple documents from the collection. + * + * @param values The keys of the documents or the documents themselves + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> deleteDocuments(Iterable values); + + /** + * Deletes multiple documents from the collection. + * + * @param values The keys of the documents or the documents themselves + * @param options Additional options + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> deleteDocuments( + Iterable values, DocumentDeleteOptions options); + + /** + * Deletes multiple documents from the collection. + * + * @param values The keys of the documents or the documents themselves + * @param type Deserialization target type for the returned documents. + * @param options Additional options + * @return information about the documents + * @see API + * Documentation + */ + CompletableFuture>> deleteDocuments( + Iterable values, DocumentDeleteOptions options, Class type); + + /** + * Checks if the document exists by reading a single document head + * + * @param key The key of the document + * @return true if the document was found, otherwise false + * @see API + * Documentation + */ + CompletableFuture documentExists(String key); + + /** + * Checks if the document exists by reading a single document head + * + * @param key The key of the document + * @param options Additional options, can be null + * @return true if the document was found, otherwise false + * @see API + * Documentation + */ + CompletableFuture documentExists(String key, DocumentExistsOptions options); + + /** + * Fetches information about the index with the given {@code id} and returns it. + *
+ * Note: inverted indexes are not returned by this method. Use + * {@link ArangoCollectionAsync#getInvertedIndex(String)} instead. + * + * @param id The index-handle + * @return information about the index + * @see + * API Documentation + */ + CompletableFuture getIndex(String id); + + /** + * Fetches information about the inverted index with the given {@code id} and returns it. + * + * @param id The index-handle + * @return information about the index + * @see API Documentation + * @since ArangoDB 3.10 + */ + CompletableFuture getInvertedIndex(String id); + + /** + * Deletes the index with the given {@code id} from the collection. + * + * @param id The index-handle + * @return the id of the index + * @see + * API Documentation + */ + CompletableFuture deleteIndex(String id); + + /** + * Creates a persistent index for the collection, if it does not already exist. + * + * @param fields A list of attribute paths + * @param options Additional options, can be null + * @return information about the index + * @see API + * Documentation + */ + CompletableFuture ensurePersistentIndex(Iterable fields, PersistentIndexOptions options); + + /** + * Creates a geo-spatial index for the collection, if it does not already exist. + * + * @param fields A list of attribute paths + * @param options Additional options, can be null + * @return information about the index + * @see API + * Documentation + */ + CompletableFuture ensureGeoIndex(Iterable fields, GeoIndexOptions options); + + /** + * Creates a fulltext index for the collection, if it does not already exist. + * + * @param fields A list of attribute paths + * @param options Additional options, can be null + * @return information about the index + * @see API + * Documentation + * @deprecated since ArangoDB 3.10, use ArangoSearch or Inverted indexes instead. + */ + @Deprecated + CompletableFuture ensureFulltextIndex(Iterable fields, FulltextIndexOptions options); + + /** + * Creates a ttl index for the collection, if it does not already exist. + * + * @param fields A list of attribute paths + * @param options Additional options, can be null + * @return information about the index + * @see API + * Documentation + */ + CompletableFuture ensureTtlIndex(Iterable fields, TtlIndexOptions options); + + /** + * Creates a ZKD multi-dimensional index for the collection, if it does not already exist. + * Note that zkd indexes are an experimental feature in ArangoDB 3.9. + * + * @param fields A list of attribute paths + * @param options Additional options, can be null + * @return information about the index + * @see API Documentation + * @since ArangoDB 3.9 + */ + CompletableFuture ensureZKDIndex(Iterable fields, ZKDIndexOptions options); + + /** + * Creates an inverted index for the collection, if it does not already exist. + * + * @param options index creation options + * @return information about the index + * @see API Documentation + * @since ArangoDB 3.10 + */ + CompletableFuture ensureInvertedIndex(InvertedIndexOptions options); + + /** + * Fetches a list of all indexes on this collection. + *
+ * Note: inverted indexes are not returned by this method. Use + * {@link ArangoCollectionAsync#getInvertedIndexes()} instead. + * + * @return information about the indexes + * @see API + * Documentation + */ + CompletableFuture> getIndexes(); + + /** + * Fetches a list of all inverted indexes on this collection. + * + * @return information about the indexes + * @see API + * Documentation + * @since ArangoDB 3.10 + */ + CompletableFuture> getInvertedIndexes(); + + /** + * Checks whether the collection exists + * + * @return true if the collection exists, otherwise false + */ + CompletableFuture exists(); + + /** + * Removes all documents from the collection, but leaves the indexes intact + * + * @return information about the collection + * @see API + * Documentation + */ + CompletableFuture truncate(); + + /** + * Removes all documents from the collection, but leaves the indexes intact + * + * @param options + * @return information about the collection + * @see API + * Documentation + * @since ArangoDB 3.5.0 + */ + CompletableFuture truncate(CollectionTruncateOptions options); + + /** + * Counts the documents in a collection + * + * @return information about the collection, including the number of documents + * @see API + * Documentation + */ + CompletableFuture count(); + + /** + * Counts the documents in a collection + * + * @param options + * @return information about the collection, including the number of documents + * @see API + * Documentation + * @since ArangoDB 3.5.0 + */ + CompletableFuture count(CollectionCountOptions options); + + /** + * Creates a collection for this collection's name, then returns collection information from the server. + * + * @return information about the collection + * @see API + * Documentation + */ + CompletableFuture create(); + + /** + * Creates a collection with the given {@code options} for this collection's name, then returns collection + * information from the server. + * + * @param options Additional options, can be null + * @return information about the collection + * @see API + * Documentation + */ + CompletableFuture create(CollectionCreateOptions options); + + /** + * Deletes the collection from the database. + * + * @see API + * Documentation + */ + CompletableFuture drop(); + + /** + * Deletes the collection from the database. + * + * @param isSystem Whether or not the collection to drop is a system collection. This parameter must be set to + * true in + * order to drop a system collection. + * @see API + * Documentation + * @since ArangoDB 3.1.0 + */ + CompletableFuture drop(boolean isSystem); + + /** + * Returns information about the collection + * + * @return information about the collection + * @see API + * Documentation + */ + CompletableFuture getInfo(); + + /** + * Reads the properties of the specified collection + * + * @return properties of the collection + * @see API + * Documentation + */ + CompletableFuture getProperties(); + + /** + * Changes the properties of the collection + * + * @param options Additional options, can be null + * @return properties of the collection + * @see API + * Documentation + */ + CompletableFuture changeProperties(CollectionPropertiesOptions options); + + /** + * Renames the collection + * + * @param newName The new name + * @return information about the collection + * @see API + * Documentation + */ + CompletableFuture rename(String newName); + + /** + * Returns the responsible shard for the document. + * Please note that this API is only meaningful and available on a cluster coordinator. + * + * @param value A projection of the document containing at least the shard key (_key or a custom attribute) for + * which the responsible shard should be determined + * @return information about the responsible shard + * @see + * + * API Documentation + * @since ArangoDB 3.5.0 + */ + CompletableFuture getResponsibleShard(final Object value); + + /** + * Retrieve the collections revision + * + * @return information about the collection, including the collections revision + * @see + * API + * Documentation + */ + CompletableFuture getRevision(); + + /** + * Grants or revoke access to the collection for user user. You need permission to the _system database in order to + * execute this call. + * + * @param user The name of the user + * @param permissions The permissions the user grant + * @see API + * Documentation + */ + CompletableFuture grantAccess(String user, Permissions permissions); + + /** + * Revokes access to the collection for user user. You need permission to the _system database in order to execute + * this call. + * + * @param user The name of the user + * @see API + * Documentation + */ + CompletableFuture revokeAccess(String user); + + /** + * Clear the collection access level, revert back to the default access level. + * + * @param user The name of the user + * @see API + * Documentation + * @since ArangoDB 3.2.0 + */ + CompletableFuture resetAccess(String user); + + /** + * Get the collection access level + * + * @param user The name of the user + * @return permissions of the user + * @see + * + * API Documentation + * @since ArangoDB 3.2.0 + */ + CompletableFuture getPermissions(String user); + +} diff --git a/core/src/main/java/com/arangodb/ArangoDBAsync.java b/core/src/main/java/com/arangodb/ArangoDBAsync.java index 282a1cfbb..620113195 100644 --- a/core/src/main/java/com/arangodb/ArangoDBAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDBAsync.java @@ -34,9 +34,17 @@ public interface ArangoDBAsync extends ArangoSerdeAccessor { /** - * @return the synchronous version of this class + * Releases all connections to the server and clear the connection pool. */ - ArangoDB sync(); + void shutdown(); + + /** + * Updates the JWT used for requests authorization. It does not change already existing VST connections, since VST + * connections are authenticated during the initialization phase. + * + * @param jwt token to use + */ + void updateJwt(String jwt); // /** // * Returns a {@code ArangoDatabase} instance for the {@code _system} database. diff --git a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java new file mode 100644 index 000000000..d0ec27bf6 --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java @@ -0,0 +1,354 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; +import com.arangodb.model.*; +import com.arangodb.model.arangosearch.AnalyzerDeleteOptions; +import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; +import com.arangodb.model.arangosearch.SearchAliasCreateOptions; + +import javax.annotation.concurrent.ThreadSafe; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +/** + * Asynchronous version of {@link ArangoDatabase} + */ +@ThreadSafe +public interface ArangoDatabaseAsync extends ArangoSerdeAccessor { + + /** + * @return main entry point for async API + */ + ArangoDBAsync arango(); + + /** + * @return database name + */ + String name(); + + /** + * Asynchronous version of {@link ArangoDatabase#getVersion()} + */ + CompletableFuture getVersion(); + + /** + * Asynchronous version of {@link ArangoDatabase#getEngine()} + */ + CompletableFuture getEngine(); + + /** + * Asynchronous version of {@link ArangoDatabase#exists()} + */ + CompletableFuture exists(); + + /** + * Asynchronous version of {@link ArangoDatabase#getAccessibleDatabases()} + */ + CompletableFuture> getAccessibleDatabases(); + +// /** +// * Returns a {@code ArangoCollection} instance for the given collection name. +// * +// * @param name Name of the collection +// * @return collection handler +// */ +// ArangoCollection collection(String name); + + /** + * Asynchronous version of {@link ArangoDatabase#createCollection(String)} + */ + CompletableFuture createCollection(String name); + + /** + * Asynchronous version of {@link ArangoDatabase#createCollection(String, CollectionCreateOptions)} + */ + CompletableFuture createCollection(String name, CollectionCreateOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#getCollections()} + */ + CompletableFuture> getCollections(); + + /** + * Asynchronous version of {@link ArangoDatabase#getCollections(CollectionsReadOptions)} + */ + CompletableFuture> getCollections(CollectionsReadOptions options); + +// /** +// * Asynchronous version of {@link ArangoDatabase#getIndex(String)} +// */ +// CompletableFuture getIndex(String id); +// +// /** +// * Asynchronous version of {@link ArangoDatabase#deleteIndex(String)} +// */ +// CompletableFuture deleteIndex(String id); + + /** + * Asynchronous version of {@link ArangoDatabase#create()} + */ + CompletableFuture create(); + + /** + * Asynchronous version of {@link ArangoDatabase#drop()} + */ + CompletableFuture drop(); + + /** + * Asynchronous version of {@link ArangoDatabase#grantAccess(String, Permissions)} + */ + CompletableFuture grantAccess(String user, Permissions permissions); + + /** + * Asynchronous version of {@link ArangoDatabase#grantAccess(String)} + */ + CompletableFuture grantAccess(String user); + + /** + * Asynchronous version of {@link ArangoDatabase#revokeAccess(String)} + */ + CompletableFuture revokeAccess(String user); + + /** + * Asynchronous version of {@link ArangoDatabase#resetAccess(String)} + */ + CompletableFuture resetAccess(String user); + + /** + * Asynchronous version of {@link ArangoDatabase#grantDefaultCollectionAccess(String, Permissions)} + */ + CompletableFuture grantDefaultCollectionAccess(String user, Permissions permissions); + + /** + * Asynchronous version of {@link ArangoDatabase#getPermissions(String)} + */ + CompletableFuture getPermissions(String user); + +// CompletableFuture> query(String query, Class type, Map bindVars, AqlQueryOptions options); +// +// CompletableFuture> query(String query, Class type, AqlQueryOptions options); +// +// CompletableFuture> query(String query, Class type, Map bindVars); +// +// CompletableFuture> query(String query, Class type); +// +// CompletableFuture> cursor(String cursorId, Class type); +// +// CompletableFuture> cursor(String cursorId, Class type, String nextBatchId); + + /** + * Asynchronous version of {@link ArangoDatabase#explainQuery(String, Map, AqlQueryExplainOptions)} + */ + CompletableFuture explainQuery(String query, Map bindVars, AqlQueryExplainOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#parseQuery(String)} + */ + CompletableFuture parseQuery(String query); + + /** + * Asynchronous version of {@link ArangoDatabase#clearQueryCache()} + */ + CompletableFuture clearQueryCache(); + + /** + * Asynchronous version of {@link ArangoDatabase#getQueryCacheProperties()} + */ + CompletableFuture getQueryCacheProperties(); + + /** + * Asynchronous version of {@link ArangoDatabase#setQueryCacheProperties(QueryCachePropertiesEntity)} + */ + CompletableFuture setQueryCacheProperties(QueryCachePropertiesEntity properties); + + /** + * Asynchronous version of {@link ArangoDatabase#getQueryTrackingProperties()} + */ + CompletableFuture getQueryTrackingProperties(); + + /** + * Asynchronous version of {@link ArangoDatabase#setQueryTrackingProperties(QueryTrackingPropertiesEntity)} + */ + CompletableFuture setQueryTrackingProperties(QueryTrackingPropertiesEntity properties); + + /** + * Asynchronous version of {@link ArangoDatabase#getCurrentlyRunningQueries()} + */ + CompletableFuture> getCurrentlyRunningQueries(); + + /** + * Asynchronous version of {@link ArangoDatabase#getSlowQueries()} + */ + CompletableFuture> getSlowQueries(); + + /** + * Asynchronous version of {@link ArangoDatabase#clearSlowQueries()} + */ + CompletableFuture clearSlowQueries(); + + /** + * Asynchronous version of {@link ArangoDatabase#killQuery(String)} + */ + CompletableFuture killQuery(String id); + + /** + * Asynchronous version of {@link ArangoDatabase#createAqlFunction(String, String, AqlFunctionCreateOptions)} + */ + CompletableFuture createAqlFunction(String name, String code, AqlFunctionCreateOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#deleteAqlFunction(String, AqlFunctionDeleteOptions)} + */ + CompletableFuture deleteAqlFunction(String name, AqlFunctionDeleteOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#getAqlFunctions(AqlFunctionGetOptions)} + */ + CompletableFuture> getAqlFunctions(AqlFunctionGetOptions options); + +// /** +// * Returns a {@code ArangoGraph} instance for the given graph name. +// * +// * @param name Name of the graph +// * @return graph handler +// */ +// ArangoGraph graph(String name); + + /** + * Asynchronous version of {@link ArangoDatabase#createGraph(String, Iterable)} + */ + CompletableFuture createGraph(String name, Iterable edgeDefinitions); + + /** + * Asynchronous version of {@link ArangoDatabase#createGraph(String, Iterable, GraphCreateOptions)} + */ + CompletableFuture createGraph(String name, Iterable edgeDefinitions, GraphCreateOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#getGraphs()} + */ + CompletableFuture> getGraphs(); + + /** + * Asynchronous version of {@link ArangoDatabase#transaction(String, Class, TransactionOptions)} + */ + CompletableFuture transaction(String action, Class type, TransactionOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#beginStreamTransaction(StreamTransactionOptions)} + */ + CompletableFuture beginStreamTransaction(StreamTransactionOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#abortStreamTransaction(String)} + */ + CompletableFuture abortStreamTransaction(String id); + + /** + * Asynchronous version of {@link ArangoDatabase#getStreamTransaction(String)} + */ + CompletableFuture getStreamTransaction(String id); + + /** + * Asynchronous version of {@link ArangoDatabase#getStreamTransactions()} + */ + CompletableFuture> getStreamTransactions(); + + /** + * Asynchronous version of {@link ArangoDatabase#commitStreamTransaction(String)} + */ + CompletableFuture commitStreamTransaction(String id); + + /** + * Asynchronous version of {@link ArangoDatabase#getInfo()} + */ + CompletableFuture getInfo(); + + /** + * Asynchronous version of {@link ArangoDatabase#reloadRouting()} + */ + CompletableFuture reloadRouting(); + + /** + * Asynchronous version of {@link ArangoDatabase#getViews()} + */ + CompletableFuture> getViews(); + +// /** +// * Asynchronous version of {@link ArangoDatabase#view(String)} +// */ +// CompletableFuture view(String name); +// +// /** +// * Asynchronous version of {@link ArangoDatabase#arangoSearch(String)} +// */ +// CompletableFuture arangoSearch(String name); +// +// /** +// * Asynchronous version of {@link ArangoDatabase#searchAlias(String)} +// */ +// CompletableFuture searchAlias(String name); + + /** + * Asynchronous version of {@link ArangoDatabase#createView(String, ViewType)} + */ + CompletableFuture createView(String name, ViewType type); + + /** + * Asynchronous version of {@link ArangoDatabase#createArangoSearch(String, ArangoSearchCreateOptions)} + */ + CompletableFuture createArangoSearch(String name, ArangoSearchCreateOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#createSearchAlias(String, SearchAliasCreateOptions)} + */ + CompletableFuture createSearchAlias(String name, SearchAliasCreateOptions options); + + /** + * Asynchronous version of {@link ArangoDatabase#createSearchAnalyzer(SearchAnalyzer)} + */ + CompletableFuture createSearchAnalyzer(SearchAnalyzer analyzer); + + /** + * Asynchronous version of {@link ArangoDatabase#getSearchAnalyzer(String)} + */ + CompletableFuture getSearchAnalyzer(String name); + + /** + * Asynchronous version of {@link ArangoDatabase#getSearchAnalyzers()} + */ + CompletableFuture> getSearchAnalyzers(); + + /** + * Asynchronous version of {@link ArangoDatabase#deleteSearchAnalyzer(String)} + */ + CompletableFuture deleteSearchAnalyzer(String name); + + /** + * Asynchronous version of {@link ArangoDatabase#deleteSearchAnalyzer(String, AnalyzerDeleteOptions)} + */ + CompletableFuture deleteSearchAnalyzer(String name, AnalyzerDeleteOptions options); + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java index 25f045615..d549b45a0 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java @@ -39,11 +39,22 @@ public class ArangoDBAsyncImpl extends InternalArangoDB implements ArangoDBAsync { private final ArangoDB arangoDB; + public ArangoDBAsyncImpl(final ArangoDBImpl arangoDB) { super(arangoDB); this.arangoDB = arangoDB; } + @Override + public void shutdown() { + arangoDB.shutdown(); + } + + @Override + public void updateJwt(String jwt) { + arangoDB.updateJwt(jwt); + } + // @Override // public ArangoDatabase db() { // return db(ArangoRequestParam.SYSTEM); @@ -59,11 +70,6 @@ public ArangoDBAsyncImpl(final ArangoDBImpl arangoDB) { // return new ArangoMetricsImpl(executorAsync().getQueueTimeMetrics()); // } - @Override - public ArangoDB sync() { - return arangoDB; - } - @Override public CompletableFuture createDatabase(final String dbName) { return createDatabase(new DBCreateOptions().name(dbName)); diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java new file mode 100644 index 000000000..b07acaeaa --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -0,0 +1,433 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.ArangoDBAsync; +import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabaseAsync; +import com.arangodb.entity.*; +import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; +import com.arangodb.model.*; +import com.arangodb.model.arangosearch.AnalyzerDeleteOptions; +import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; +import com.arangodb.model.arangosearch.SearchAliasCreateOptions; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import static com.arangodb.internal.serde.SerdeUtils.constructListType; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +public class ArangoDatabaseAsyncImpl extends InternalArangoDatabase implements ArangoDatabaseAsync { + + private final ArangoDBAsync arangoDB; + + protected ArangoDatabaseAsyncImpl(final ArangoDBAsyncImpl arangoDB, final String name) { + super(arangoDB, name); + this.arangoDB = arangoDB; + } + + @Override + public ArangoDBAsync arango() { + return arangoDB; + } + + @Override + public CompletableFuture getVersion() { + return executorAsync().execute(getVersionRequest(), ArangoDBVersion.class); + } + + @Override + public CompletableFuture getEngine() { + return executorAsync().execute(getEngineRequest(), ArangoDBEngine.class); + } + + @Override + public CompletableFuture exists() { + return getInfo() + .handle((result, ex) -> { + if (result != null) { + return true; + } + + if (ex instanceof ArangoDBException) { + ArangoDBException e = (ArangoDBException) ex.getCause(); + if (ArangoErrors.ERROR_ARANGO_DATABASE_NOT_FOUND.equals(e.getErrorNum())) { + return false; + } + } + + throw ArangoDBException.wrap(ex); + }); + } + + @Override + public CompletableFuture> getAccessibleDatabases() { + return executorAsync().execute(getAccessibleDatabasesRequest(), getDatabaseResponseDeserializer()); + } + +// @Override +// public ArangoCollection collection(final String name) { +// return new ArangoCollectionImpl(this, name); +// } + + @Override + public CompletableFuture createCollection(final String name) { + return executorAsync().execute(createCollectionRequest(name, new CollectionCreateOptions()), CollectionEntity.class); + } + + @Override + public CompletableFuture createCollection(final String name, final CollectionCreateOptions options) { + return executorAsync().execute(createCollectionRequest(name, options), CollectionEntity.class); + } + + @Override + public CompletableFuture> getCollections() { + return executorAsync() + .execute(getCollectionsRequest(new CollectionsReadOptions()), getCollectionsResponseDeserializer()); + } + + @Override + public CompletableFuture> getCollections(final CollectionsReadOptions options) { + return executorAsync().execute(getCollectionsRequest(options), getCollectionsResponseDeserializer()); + } + +// @Override +// public CompletableFuture getIndex(final String id) { +// DocumentUtil.validateIndexId(id); +// final String[] split = id.split("/"); +// return collection(split[0]).getIndex(split[1]); +// } + +// @Override +// public CompletableFuture deleteIndex(final String id) { +// DocumentUtil.validateIndexId(id); +// final String[] split = id.split("/"); +// return collection(split[0]).deleteIndex(split[1]); +// } + + @Override + public CompletableFuture create() { + return arango().createDatabase(name()); + } + + @Override + public CompletableFuture drop() { + return executorAsync().execute(dropRequest(), createDropResponseDeserializer()); + } + + @Override + public CompletableFuture grantAccess(final String user, final Permissions permissions) { + return executorAsync().execute(grantAccessRequest(user, permissions), Void.class); + } + + @Override + public CompletableFuture grantAccess(final String user) { + return executorAsync().execute(grantAccessRequest(user, Permissions.RW), Void.class); + } + + @Override + public CompletableFuture revokeAccess(final String user) { + return executorAsync().execute(grantAccessRequest(user, Permissions.NONE), Void.class); + } + + @Override + public CompletableFuture resetAccess(final String user) { + return executorAsync().execute(resetAccessRequest(user), Void.class); + } + + @Override + public CompletableFuture grantDefaultCollectionAccess(final String user, final Permissions permissions) { + return executorAsync().execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); + } + + @Override + public CompletableFuture getPermissions(final String user) { + return executorAsync().execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); + } + +// @Override +// public ArangoCursor query( +// final String query, final Class type, final Map bindVars, final AqlQueryOptions options) { +// final InternalRequest request = queryRequest(query, bindVars, options); +// final HostHandle hostHandle = new HostHandle(); +// final InternalCursorEntity result = executorAsync().execute(request, internalCursorEntityDeserializer(), hostHandle); +// return createCursor(result, type, options, hostHandle); +// } +// +// @Override +// public ArangoCursor query(final String query, final Class type, final Map bindVars) { +// return query(query, type, bindVars, new AqlQueryOptions()); +// } +// +// @Override +// public ArangoCursor query(final String query, final Class type, final AqlQueryOptions options) { +// return query(query, type, null, options); +// } +// +// @Override +// public ArangoCursor query(final String query, final Class type) { +// return query(query, type, null, new AqlQueryOptions()); +// } +// +// @Override +// public ArangoCursor cursor(final String cursorId, final Class type) { +// final HostHandle hostHandle = new HostHandle(); +// final InternalCursorEntity result = executorAsync().execute( +// queryNextRequest(cursorId, null), +// internalCursorEntityDeserializer(), +// hostHandle); +// return createCursor(result, type, null, hostHandle); +// } +// +// @Override +// public ArangoCursor cursor(final String cursorId, final Class type, final String nextBatchId) { +// final HostHandle hostHandle = new HostHandle(); +// final InternalCursorEntity result = executorAsync().execute( +// queryNextByBatchIdRequest(cursorId, nextBatchId, null), +// internalCursorEntityDeserializer(), +// hostHandle); +// return createCursor(result, type, null, hostHandle); +// } +// +// private ArangoCursor createCursor( +// final InternalCursorEntity result, +// final Class type, +// final AqlQueryOptions options, +// final HostHandle hostHandle) { +// +// final ArangoCursorExecute execute = new ArangoCursorExecute() { +// @Override +// public InternalCursorEntity next(final String id, final String nextBatchId) { +// InternalRequest request = nextBatchId == null ? +// queryNextRequest(id, options) : queryNextByBatchIdRequest(id, nextBatchId, options); +// return executorAsync().execute(request, internalCursorEntityDeserializer(), hostHandle); +// } +// +// @Override +// public void close(final String id) { +// executorAsync().execute(queryCloseRequest(id, options), Void.class, hostHandle); +// } +// }; +// +// return new ArangoCursorImpl<>(this, execute, type, result); +// } + + @Override + public CompletableFuture explainQuery( + final String query, final Map bindVars, final AqlQueryExplainOptions options) { + return executorAsync().execute(explainQueryRequest(query, bindVars, options), AqlExecutionExplainEntity.class); + } + + @Override + public CompletableFuture parseQuery(final String query) { + return executorAsync().execute(parseQueryRequest(query), AqlParseEntity.class); + } + + @Override + public CompletableFuture clearQueryCache() { + return executorAsync().execute(clearQueryCacheRequest(), Void.class); + } + + @Override + public CompletableFuture getQueryCacheProperties() { + return executorAsync().execute(getQueryCachePropertiesRequest(), QueryCachePropertiesEntity.class); + } + + @Override + public CompletableFuture setQueryCacheProperties(final QueryCachePropertiesEntity properties) { + return executorAsync().execute(setQueryCachePropertiesRequest(properties), QueryCachePropertiesEntity.class); + } + + @Override + public CompletableFuture getQueryTrackingProperties() { + return executorAsync().execute(getQueryTrackingPropertiesRequest(), QueryTrackingPropertiesEntity.class); + } + + @Override + public CompletableFuture setQueryTrackingProperties(final QueryTrackingPropertiesEntity properties) { + return executorAsync().execute(setQueryTrackingPropertiesRequest(properties), QueryTrackingPropertiesEntity.class); + } + + @Override + public CompletableFuture> getCurrentlyRunningQueries() { + return executorAsync().execute(getCurrentlyRunningQueriesRequest(), + constructListType(QueryEntity.class)); + } + + @Override + public CompletableFuture> getSlowQueries() { + return executorAsync().execute(getSlowQueriesRequest(), + constructListType(QueryEntity.class)); + } + + @Override + public CompletableFuture clearSlowQueries() { + return executorAsync().execute(clearSlowQueriesRequest(), Void.class); + } + + @Override + public CompletableFuture killQuery(final String id) { + return executorAsync().execute(killQueryRequest(id), Void.class); + } + + @Override + public CompletableFuture createAqlFunction( + final String name, final String code, final AqlFunctionCreateOptions options) { + return executorAsync().execute(createAqlFunctionRequest(name, code, options), Void.class); + } + + @Override + public CompletableFuture deleteAqlFunction(final String name, final AqlFunctionDeleteOptions options) { + return executorAsync().execute(deleteAqlFunctionRequest(name, options), deleteAqlFunctionResponseDeserializer()); + } + + @Override + public CompletableFuture> getAqlFunctions(final AqlFunctionGetOptions options) { + return executorAsync().execute(getAqlFunctionsRequest(options), getAqlFunctionsResponseDeserializer()); + } + +// @Override +// public ArangoGraph graph(final String name) { +// return new ArangoGraphImpl(this, name); +// } + + @Override + public CompletableFuture createGraph(final String name, final Iterable edgeDefinitions) { + return createGraph(name, edgeDefinitions, new GraphCreateOptions()); + } + + @Override + public CompletableFuture createGraph( + final String name, final Iterable edgeDefinitions, final GraphCreateOptions options) { + return executorAsync().execute(createGraphRequest(name, edgeDefinitions, options), createGraphResponseDeserializer()); + } + + @Override + public CompletableFuture> getGraphs() { + return executorAsync().execute(getGraphsRequest(), getGraphsResponseDeserializer()); + } + + @Override + public CompletableFuture transaction(final String action, final Class type, final TransactionOptions options) { + return executorAsync().execute(transactionRequest(action, options), transactionResponseDeserializer(type)); + } + + @Override + public CompletableFuture beginStreamTransaction(StreamTransactionOptions options) { + return executorAsync().execute(beginStreamTransactionRequest(options), streamTransactionResponseDeserializer()); + } + + @Override + public CompletableFuture abortStreamTransaction(String id) { + return executorAsync().execute(abortStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + } + + @Override + public CompletableFuture getStreamTransaction(String id) { + return executorAsync().execute(getStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + } + + @Override + public CompletableFuture> getStreamTransactions() { + return executorAsync().execute(getStreamTransactionsRequest(), transactionsResponseDeserializer()); + } + + @Override + public CompletableFuture commitStreamTransaction(String id) { + return executorAsync().execute(commitStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + } + + @Override + public CompletableFuture getInfo() { + return executorAsync().execute(getInfoRequest(), getInfoResponseDeserializer()); + } + + @Override + public CompletableFuture reloadRouting() { + return executorAsync().execute(reloadRoutingRequest(), Void.class); + } + + @Override + public CompletableFuture> getViews() { + return executorAsync().execute(getViewsRequest(), getViewsResponseDeserializer()); + } + +// @Override +// public CompletableFuture view(final String name) { +// return new ArangoViewImpl(this, name); +// } + +// @Override +// public ArangoSearch arangoSearch(final String name) { +// return new ArangoSearchImpl(this, name); +// } + +// @Override +// public SearchAlias searchAlias(String name) { +// return new SearchAliasImpl(this, name); +// } + + @Override + public CompletableFuture createView(final String name, final ViewType type) { + return executorAsync().execute(createViewRequest(name, type), ViewEntity.class); + } + + @Override + public CompletableFuture createArangoSearch(final String name, final ArangoSearchCreateOptions options) { + return executorAsync().execute(createArangoSearchRequest(name, options), ViewEntity.class); + } + + @Override + public CompletableFuture createSearchAlias(String name, SearchAliasCreateOptions options) { + return executorAsync().execute(createSearchAliasRequest(name, options), ViewEntity.class); + } + + @Override + public CompletableFuture createSearchAnalyzer(SearchAnalyzer analyzer) { + return executorAsync().execute(createAnalyzerRequest(analyzer), SearchAnalyzer.class); + } + + @Override + public CompletableFuture getSearchAnalyzer(String name) { + return executorAsync().execute(getAnalyzerRequest(name), SearchAnalyzer.class); + } + + @Override + public CompletableFuture> getSearchAnalyzers() { + return executorAsync().execute(getAnalyzersRequest(), getSearchAnalyzersResponseDeserializer()); + } + + @Override + public CompletableFuture deleteSearchAnalyzer(String name) { + return deleteSearchAnalyzer(name, null); + } + + @Override + public CompletableFuture deleteSearchAnalyzer(String name, AnalyzerDeleteOptions options) { + return executorAsync().execute(deleteAnalyzerRequest(name, options), Void.class); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java index 24422febb..7c5cd151f 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java @@ -43,8 +43,16 @@ */ public class ArangoDatabaseImpl extends InternalArangoDatabase implements ArangoDatabase { + private final ArangoDB arangoDB; + protected ArangoDatabaseImpl(final ArangoDBImpl arangoDB, final String name) { super(arangoDB, name); + this.arangoDB = arangoDB; + } + + @Override + public ArangoDB arango() { + return arangoDB; } @Override diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java index f8653c77f..9d54a5ae3 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java @@ -20,7 +20,6 @@ package com.arangodb.internal; -import com.arangodb.ArangoDB; import com.arangodb.entity.*; import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; @@ -60,24 +59,19 @@ public abstract class InternalArangoDatabase extends ArangoExecuteable { private static final String TRANSACTION_ID = "x-arango-trx-id"; private final String name; - private final ArangoDBImpl arango; - protected InternalArangoDatabase(final ArangoDBImpl arango, final String name) { - super(arango); - this.arango = arango; + protected InternalArangoDatabase(final ArangoExecuteable executeable, final String name) { + super(executeable); this.name = name; } - public ArangoDB arango() { - return arango; - } - public String name() { return name; } protected ResponseDeserializer> getDatabaseResponseDeserializer() { - return arango.getDatabaseResponseDeserializer(); + return response -> getSerde().deserialize(response.getBody(), ArangoResponseField.RESULT_JSON_POINTER, + constructListType(String.class)); } protected InternalRequest getAccessibleDatabasesRequest() { From 3b47370e424f04c4fe0f0d12b780c0267fcb5da9 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 12 Oct 2023 10:26:40 +0200 Subject: [PATCH 17/62] ArangoDBAsyncTest --- .../main/java/com/arangodb/ArangoDBAsync.java | 54 +- .../arangodb/internal/ArangoDBAsyncImpl.java | 49 +- .../java/com/arangodb/ArangoDBAsyncTest.java | 465 +++++++++--------- .../test/java/com/arangodb/BaseJunit5.java | 4 + 4 files changed, 287 insertions(+), 285 deletions(-) diff --git a/core/src/main/java/com/arangodb/ArangoDBAsync.java b/core/src/main/java/com/arangodb/ArangoDBAsync.java index 620113195..6224eda84 100644 --- a/core/src/main/java/com/arangodb/ArangoDBAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDBAsync.java @@ -46,21 +46,21 @@ public interface ArangoDBAsync extends ArangoSerdeAccessor { */ void updateJwt(String jwt); -// /** -// * Returns a {@code ArangoDatabase} instance for the {@code _system} database. -// * -// * @return database handler -// */ -// ArangoDatabase db(); -// -// /** -// * Returns a {@code ArangoDatabase} instance for the given database name. -// * -// * @param name Name of the database -// * @return database handler -// */ -// ArangoDatabase db(String name); -// + /** + * Returns a {@code ArangoDatabase} instance for the {@code _system} database. + * + * @return database handler + */ + ArangoDatabaseAsync db(); + + /** + * Returns a {@code ArangoDatabase} instance for the given database name. + * + * @param name Name of the database + * @return database handler + */ + ArangoDatabaseAsync db(String name); + // /** // * @return entry point for accessing client metrics // */ @@ -81,25 +81,25 @@ public interface ArangoDBAsync extends ArangoSerdeAccessor { */ CompletableFuture> getDatabases(); -// /** -// * Asynchronous version of {@link ArangoDB#getAccessibleDatabases()} -// */ -// CompletableFuture> getAccessibleDatabases(); + /** + * Asynchronous version of {@link ArangoDB#getAccessibleDatabases()} + */ + CompletableFuture> getAccessibleDatabases(); /** * Asynchronous version of {@link ArangoDB#getAccessibleDatabasesFor(String)} */ CompletableFuture> getAccessibleDatabasesFor(String user); -// /** -// * Asynchronous version of {@link ArangoDB#getVersion()} -// */ -// CompletableFuture getVersion(); + /** + * Asynchronous version of {@link ArangoDB#getVersion()} + */ + CompletableFuture getVersion(); -// /** -// * Asynchronous version of {@link ArangoDB#getEngine()} -// */ -// CompletableFuture getEngine(); + /** + * Asynchronous version of {@link ArangoDB#getEngine()} + */ + CompletableFuture getEngine(); /** * Asynchronous version of {@link ArangoDB#getRole()} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java index d549b45a0..14753cb0e 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java @@ -20,10 +20,7 @@ package com.arangodb.internal; -import com.arangodb.ArangoDB; -import com.arangodb.ArangoDBAsync; -import com.arangodb.Request; -import com.arangodb.Response; +import com.arangodb.*; import com.arangodb.entity.*; import com.arangodb.internal.serde.SerdeUtils; import com.arangodb.model.*; @@ -55,16 +52,16 @@ public void updateJwt(String jwt) { arangoDB.updateJwt(jwt); } -// @Override -// public ArangoDatabase db() { -// return db(ArangoRequestParam.SYSTEM); -// } -// -// @Override -// public ArangoDatabase db(final String dbName) { -// return new ArangoDatabaseImpl(this, dbName); -// } -// + @Override + public ArangoDatabaseAsync db() { + return db(ArangoRequestParam.SYSTEM); + } + + @Override + public ArangoDatabaseAsync db(final String dbName) { + return new ArangoDatabaseAsyncImpl(this, dbName); + } + // @Override // public ArangoMetrics metrics() { // return new ArangoMetricsImpl(executorAsync().getQueueTimeMetrics()); @@ -85,10 +82,10 @@ public CompletableFuture> getDatabases() { return executorAsync().execute(getDatabasesRequest(ArangoRequestParam.SYSTEM), getDatabaseResponseDeserializer()); } -// @Override -// public CompletableFuture> getAccessibleDatabases() { -// return db().getAccessibleDatabases(); -// } + @Override + public CompletableFuture> getAccessibleDatabases() { + return db().getAccessibleDatabases(); + } @Override public CompletableFuture> getAccessibleDatabasesFor(final String user) { @@ -96,15 +93,15 @@ public CompletableFuture> getAccessibleDatabasesFor(final Str getAccessibleDatabasesForResponseDeserializer()); } -// @Override -// public CompletableFuture getVersion() { -// return db().getVersion(); -// } + @Override + public CompletableFuture getVersion() { + return db().getVersion(); + } -// @Override -// public CompletableFuture getEngine() { -// return db().getEngine(); -// } + @Override + public CompletableFuture getEngine() { + return db().getEngine(); + } @Override public CompletableFuture getRole() { diff --git a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java index da241a4c6..14fd49c3a 100644 --- a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java @@ -20,6 +20,7 @@ package com.arangodb; +import com.arangodb.config.ConfigUtils; import com.arangodb.entity.*; import com.arangodb.internal.ArangoRequestParam; import com.arangodb.internal.serde.SerdeUtils; @@ -30,6 +31,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -66,47 +68,47 @@ static void shutdown() { dropDB(DB2); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") -// void getVersion(ArangoDB arangoDB) { -// final ArangoDBVersion version = arangoDB.async().getVersion(); -// assertThat(version.getServer()).isNotNull(); -// assertThat(version.getVersion()).isNotNull(); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void getVersion(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + final ArangoDBVersion version = arangoDB.getVersion().get(); + assertThat(version.getServer()).isNotNull(); + assertThat(version.getVersion()).isNotNull(); + } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void createAndDeleteDatabase(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void createAndDeleteDatabase(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { final String dbName = rndDbName(); - final Boolean resultCreate = arangoDB.async().createDatabase(dbName).get(); + final Boolean resultCreate = arangoDB.createDatabase(dbName).get(); assertThat(resultCreate).isTrue(); -// final Boolean resultDelete = arangoDB.async().db(dbName).drop(); -// assertThat(resultDelete).isTrue(); + final Boolean resultDelete = arangoDB.db(dbName).drop().get(); + assertThat(resultDelete).isTrue(); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void createWithNotNormalizedName(ArangoDB arangoDB) { + @MethodSource("asyncArangos") + void createWithNotNormalizedName(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(supportsExtendedDbNames()); final String dbName = "testDB-\u006E\u0303\u00f1"; String normalized = UnicodeUtils.normalize(dbName); - arangoDB.async().createDatabase(normalized); -// arangoDB.async().db(normalized).drop(); + arangoDB.createDatabase(normalized); + arangoDB.db(normalized).drop().get(); - Throwable thrown = catchThrowable(() -> arangoDB.async().createDatabase(dbName)); + Throwable thrown = catchThrowable(() -> arangoDB.createDatabase(dbName)); assertThat(thrown) .isInstanceOf(ArangoDBException.class) .hasMessageContaining("normalized"); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void createDatabaseWithOptions(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void createDatabaseWithOptions(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isCluster()); assumeTrue(isAtLeastVersion(3, 6)); final String dbName = rndDbName(); - final Boolean resultCreate = arangoDB.async().createDatabase(new DBCreateOptions() + final Boolean resultCreate = arangoDB.createDatabase(new DBCreateOptions() .name(dbName) .options(new DatabaseOptions() .writeConcern(2) @@ -116,24 +118,24 @@ void createDatabaseWithOptions(ArangoDB arangoDB) throws ExecutionException, Int ).get(); assertThat(resultCreate).isTrue(); -// DatabaseEntity info = arangoDB.async().db(dbName).getInfo(); -// assertThat(info.getReplicationFactor().get()).isEqualTo(2); -// assertThat(info.getWriteConcern()).isEqualTo(2); -// assertThat(info.getSharding()).isEmpty(); -// -// final Boolean resultDelete = arangoDB.async().db(dbName).drop(); -// assertThat(resultDelete).isTrue(); + DatabaseEntity info = arangoDB.db(dbName).getInfo().get(); + assertThat(info.getReplicationFactor().get()).isEqualTo(2); + assertThat(info.getWriteConcern()).isEqualTo(2); + assertThat(info.getSharding()).isEmpty(); + + final Boolean resultDelete = arangoDB.db(dbName).drop().get(); + assertThat(resultDelete).isTrue(); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void createDatabaseWithOptionsSatellite(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void createDatabaseWithOptionsSatellite(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isCluster()); assumeTrue(isEnterprise()); assumeTrue(isAtLeastVersion(3, 6)); final String dbName = rndDbName(); - final Boolean resultCreate = arangoDB.async().createDatabase(new DBCreateOptions() + final Boolean resultCreate = arangoDB.createDatabase(new DBCreateOptions() .name(dbName) .options(new DatabaseOptions() .writeConcern(2) @@ -143,21 +145,21 @@ void createDatabaseWithOptionsSatellite(ArangoDB arangoDB) throws ExecutionExcep ).get(); assertThat(resultCreate).isTrue(); -// DatabaseEntity info = arangoDB.async().db(dbName).getInfo(); -// assertThat(info.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); -// assertThat(info.getWriteConcern()).isEqualTo(2); -// assertThat(info.getSharding()).isEmpty(); -// -// final Boolean resultDelete = arangoDB.async().db(dbName).drop(); -// assertThat(resultDelete).isTrue(); + DatabaseEntity info = arangoDB.db(dbName).getInfo().get(); + assertThat(info.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + assertThat(info.getWriteConcern()).isEqualTo(2); + assertThat(info.getSharding()).isEmpty(); + + final Boolean resultDelete = arangoDB.db(dbName).drop().get(); + assertThat(resultDelete).isTrue(); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void createDatabaseWithUsers(ArangoDB arangoDB) throws InterruptedException, ExecutionException { + @MethodSource("asyncArangos") + void createDatabaseWithUsers(ArangoDBAsync arangoDB) throws InterruptedException, ExecutionException { final String dbName = rndDbName(); final Map extra = Collections.singletonMap("key", "value"); - final Boolean resultCreate = arangoDB.async().createDatabase(new DBCreateOptions() + final Boolean resultCreate = arangoDB.createDatabase(new DBCreateOptions() .name(dbName) .users(Collections.singletonList(new DatabaseUsersOptions() .active(true) @@ -168,10 +170,10 @@ void createDatabaseWithUsers(ArangoDB arangoDB) throws InterruptedException, Exe ).get(); assertThat(resultCreate).isTrue(); -// DatabaseEntity info = arangoDB.async().db(dbName).getInfo(); -// assertThat(info.getName()).isEqualTo(dbName); + DatabaseEntity info = arangoDB.db(dbName).getInfo().get(); + assertThat(info.getName()).isEqualTo(dbName); - Optional retrievedUserOptional = arangoDB.async().getUsers().get().stream() + Optional retrievedUserOptional = arangoDB.getUsers().get().stream() .filter(it -> it.getUser().equals("testUser")) .findFirst(); assertThat(retrievedUserOptional).isPresent(); @@ -183,90 +185,91 @@ void createDatabaseWithUsers(ArangoDB arangoDB) throws InterruptedException, Exe // needed for active-failover tests only Thread.sleep(2_000); - ArangoDB arangoDBTestUser = new ArangoDB.Builder() - .loadProperties(config) - .user("testUser") - .password("testPasswd") - .build(); - + // FIXME +// ArangoDBAsync arangoDBTestUser = new ArangoDB.Builder() +// .loadProperties(config) +// .user("testUser") +// .password("testPasswd") +// .build() +// .async(); // check if testUser has been created and can access the created db -// ArangoCollection collection = arangoDBTestUser.async().db(dbName).collection("col-" + UUID.randomUUID()); -// collection.create(); +// ArangoCollectionAsync collection = arangoDBTestUser.db(dbName).collection("col-" + UUID.randomUUID()); +// collection.create().get(); // arangoDBTestUser.shutdown(); -// -// final Boolean resultDelete = arangoDB.async().db(dbName).drop(); -// assertThat(resultDelete).isTrue(); + + final Boolean resultDelete = arangoDB.db(dbName).drop().get(); + assertThat(resultDelete).isTrue(); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getDatabases(ArangoDB arangoDB) throws ExecutionException, InterruptedException { - Collection dbs = arangoDB.async().getDatabases().get(); + @MethodSource("asyncArangos") + void getDatabases(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + Collection dbs = arangoDB.getDatabases().get(); assertThat(dbs).contains("_system", DB1); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") -// void getAccessibleDatabases(ArangoDB arangoDB) { -// final Collection dbs = arangoDB.async().getAccessibleDatabases(); -// assertThat(dbs).contains("_system"); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void getAccessibleDatabases(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + final Collection dbs = arangoDB.getAccessibleDatabases().get(); + assertThat(dbs).contains("_system"); + } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getAccessibleDatabasesFor(ArangoDB arangoDB) throws ExecutionException, InterruptedException { - final Collection dbs = arangoDB.async().getAccessibleDatabasesFor("root").get(); + @MethodSource("asyncArangos") + void getAccessibleDatabasesFor(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + final Collection dbs = arangoDB.getAccessibleDatabasesFor("root").get(); assertThat(dbs).contains("_system"); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void createUser(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void createUser(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { String username = "user-" + UUID.randomUUID(); - final UserEntity result = arangoDB.async().createUser(username, PW, null).get(); + final UserEntity result = arangoDB.createUser(username, PW, null).get(); assertThat(result.getUser()).isEqualTo(username); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void deleteUser(ArangoDB arangoDB) { + @MethodSource("asyncArangos") + void deleteUser(ArangoDBAsync arangoDB) { String username = "user-" + UUID.randomUUID(); - arangoDB.async().createUser(username, PW, null); - arangoDB.async().deleteUser(username); + arangoDB.createUser(username, PW, null); + arangoDB.deleteUser(username); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getUserRoot(ArangoDB arangoDB) throws ExecutionException, InterruptedException { - final UserEntity user = arangoDB.async().getUser(ROOT).get(); + @MethodSource("asyncArangos") + void getUserRoot(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + final UserEntity user = arangoDB.getUser(ROOT).get(); assertThat(user.getUser()).isEqualTo(ROOT); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getUser(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getUser(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { String username = "user-" + UUID.randomUUID(); - arangoDB.async().createUser(username, PW, null).get(); - final UserEntity user = arangoDB.async().getUser(username).get(); + arangoDB.createUser(username, PW, null).get(); + final UserEntity user = arangoDB.getUser(username).get(); assertThat(user.getUser()).isEqualTo(username); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getUsersOnlyRoot(ArangoDB arangoDB) throws ExecutionException, InterruptedException { - final Collection users = arangoDB.async().getUsers().get(); + @MethodSource("asyncArangos") + void getUsersOnlyRoot(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + final Collection users = arangoDB.getUsers().get(); assertThat(users).isNotEmpty(); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getUsers(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getUsers(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { String username = "user-" + UUID.randomUUID(); // Allow & account for pre-existing users other than ROOT: - final Collection initialUsers = arangoDB.async().getUsers().get(); + final Collection initialUsers = arangoDB.getUsers().get(); - arangoDB.async().createUser(username, PW, null).get(); - final Collection users = arangoDB.async().getUsers().get(); + arangoDB.createUser(username, PW, null).get(); + final Collection users = arangoDB.getUsers().get(); assertThat(users).hasSize(initialUsers.size() + 1); final List expected = new ArrayList<>(users.size()); @@ -283,98 +286,102 @@ void getUsers(ArangoDB arangoDB) throws ExecutionException, InterruptedException } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void updateUserNoOptions(ArangoDB arangoDB) { + @MethodSource("asyncArangos") + void updateUserNoOptions(ArangoDBAsync arangoDB) { String username = "user-" + UUID.randomUUID(); - arangoDB.async().createUser(username, PW, null); - arangoDB.async().updateUser(username, null); + arangoDB.createUser(username, PW, null); + arangoDB.updateUser(username, null); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void updateUser(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void updateUser(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { String username = "user-" + UUID.randomUUID(); final Map extra = new HashMap<>(); extra.put("hund", false); - arangoDB.async().createUser(username, PW, new UserCreateOptions().extra(extra)); + arangoDB.createUser(username, PW, new UserCreateOptions().extra(extra)); extra.put("hund", true); extra.put("mund", true); - final UserEntity user = arangoDB.async().updateUser(username, new UserUpdateOptions().extra(extra)).get(); + final UserEntity user = arangoDB.updateUser(username, new UserUpdateOptions().extra(extra)).get(); assertThat(user.getExtra()).hasSize(2); assertThat(user.getExtra()).containsKey("hund"); assertThat(Boolean.valueOf(String.valueOf(user.getExtra().get("hund")))).isTrue(); - final UserEntity user2 = arangoDB.async().getUser(username).get(); + final UserEntity user2 = arangoDB.getUser(username).get(); assertThat(user2.getExtra()).hasSize(2); assertThat(user2.getExtra()).containsKey("hund"); assertThat(Boolean.valueOf(String.valueOf(user2.getExtra().get("hund")))).isTrue(); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void replaceUser(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void replaceUser(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { String username = "user-" + UUID.randomUUID(); final Map extra = new HashMap<>(); extra.put("hund", false); - arangoDB.async().createUser(username, PW, new UserCreateOptions().extra(extra)).get(); + arangoDB.createUser(username, PW, new UserCreateOptions().extra(extra)).get(); extra.remove("hund"); extra.put("mund", true); - final UserEntity user = arangoDB.async().replaceUser(username, new UserUpdateOptions().extra(extra)).get(); + final UserEntity user = arangoDB.replaceUser(username, new UserUpdateOptions().extra(extra)).get(); assertThat(user.getExtra()).hasSize(1); assertThat(user.getExtra()).containsKey("mund"); assertThat(Boolean.valueOf(String.valueOf(user.getExtra().get("mund")))).isTrue(); - final UserEntity user2 = arangoDB.async().getUser(username).get(); + final UserEntity user2 = arangoDB.getUser(username).get(); assertThat(user2.getExtra()).hasSize(1); assertThat(user2.getExtra()).containsKey("mund"); assertThat(Boolean.valueOf(String.valueOf(user2.getExtra().get("mund")))).isTrue(); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void updateUserDefaultDatabaseAccess(ArangoDB arangoDB) { + @MethodSource("asyncArangos") + void updateUserDefaultDatabaseAccess(ArangoDBAsync arangoDB) { String username = "user-" + UUID.randomUUID(); - arangoDB.async().createUser(username, PW); - arangoDB.async().grantDefaultDatabaseAccess(username, Permissions.RW); + arangoDB.createUser(username, PW); + arangoDB.grantDefaultDatabaseAccess(username, Permissions.RW); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void updateUserDefaultCollectionAccess(ArangoDB arangoDB) { + @MethodSource("asyncArangos") + void updateUserDefaultCollectionAccess(ArangoDBAsync arangoDB) { String username = "user-" + UUID.randomUUID(); - arangoDB.async().createUser(username, PW); - arangoDB.async().grantDefaultCollectionAccess(username, Permissions.RW); + arangoDB.createUser(username, PW); + arangoDB.grantDefaultCollectionAccess(username, Permissions.RW); } -// @Test -// void authenticationFailPassword() { -// final ArangoDB arangoDB = new ArangoDB.Builder() -// .loadProperties(config) -// .password("no").jwt(null).build(); -// Throwable thrown = catchThrowable(() -> arangoDB.async().getVersion().get()); -// assertThat(thrown).isInstanceOf(ArangoDBException.class); -// assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(401); -// } + @Test + void authenticationFailPassword() { + final ArangoDBAsync arangoDB = new ArangoDB.Builder() + .loadProperties(config) + .password("no").jwt(null) + .build() + .async(); + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(401); + } -// @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") -// void authenticationFailUser() { -// final ArangoDB arangoDB = new ArangoDB.Builder() -// .loadProperties(config) -// .user("no").jwt(null).build(); -// Throwable thrown = catchThrowable(() -> arangoDB.async().getVersion().get()); -// assertThat(thrown).isInstanceOf(ArangoDBException.class); -// assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(401); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void authenticationFailUser() { + final ArangoDBAsync arangoDB = new ArangoDB.Builder() + .loadProperties(config) + .user("no").jwt(null) + .build() + .async(); + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(401); + } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void executeGetVersion(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void executeGetVersion(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { Request request = Request.builder() .db(ArangoRequestParam.SYSTEM) .method(Request.Method.GET) .path("/_api/version") .queryParam("details", "true") .build(); - final Response response = arangoDB.async().execute(request, RawJson.class).get(); + final Response response = arangoDB.execute(request, RawJson.class).get(); JsonNode body = SerdeUtils.INSTANCE.parseJson(response.getBody().get()); assertThat(body.get("version").isTextual()).isTrue(); assertThat(body.get("details").isObject()).isTrue(); @@ -386,84 +393,84 @@ void executeGetVersion(ArangoDB arangoDB) throws ExecutionException, Interrupted } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntries(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntries(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + final LogEntriesEntity logs = arangoDB.getLogEntries(null).get(); assertThat(logs.getTotal()).isPositive(); assertThat(logs.getMessages()).hasSize(logs.getTotal().intValue()); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntriesUpto(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntriesUpto(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logsUpto = arangoDB.async().getLogEntries(new LogOptions().upto(LogLevel.WARNING)).get(); + final LogEntriesEntity logsUpto = arangoDB.getLogEntries(new LogOptions().upto(LogLevel.WARNING)).get(); assertThat(logsUpto.getMessages()) .map(LogEntriesEntity.Message::getLevel) .doesNotContain("INFO"); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntriesLevel(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntriesLevel(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logsInfo = arangoDB.async().getLogEntries(new LogOptions().level(LogLevel.INFO)).get(); + final LogEntriesEntity logsInfo = arangoDB.getLogEntries(new LogOptions().level(LogLevel.INFO)).get(); assertThat(logsInfo.getMessages()) .map(LogEntriesEntity.Message::getLevel) .containsOnly("INFO"); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntriesStart(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntriesStart(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + final LogEntriesEntity logs = arangoDB.getLogEntries(null).get(); final Long firstId = logs.getMessages().get(0).getId(); - final LogEntriesEntity logsStart = arangoDB.async().getLogEntries(new LogOptions().start(firstId + 1)).get(); + final LogEntriesEntity logsStart = arangoDB.getLogEntries(new LogOptions().start(firstId + 1)).get(); assertThat(logsStart.getMessages()) .map(LogEntriesEntity.Message::getId) .doesNotContain(firstId); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntriesSize(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntriesSize(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + final LogEntriesEntity logs = arangoDB.getLogEntries(null).get(); int count = logs.getMessages().size(); assertThat(count).isPositive(); - final LogEntriesEntity logsSize = arangoDB.async().getLogEntries(new LogOptions().size(count - 1)).get(); + final LogEntriesEntity logsSize = arangoDB.getLogEntries(new LogOptions().size(count - 1)).get(); assertThat(logsSize.getMessages()).hasSize(count - 1); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntriesOffset(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntriesOffset(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); + final LogEntriesEntity logs = arangoDB.getLogEntries(null).get(); assertThat(logs.getTotal()).isPositive(); Long firstId = logs.getMessages().get(0).getId(); - final LogEntriesEntity logsOffset = arangoDB.async().getLogEntries(new LogOptions().offset(1)).get(); + final LogEntriesEntity logsOffset = arangoDB.getLogEntries(new LogOptions().offset(1)).get(); assertThat(logsOffset.getMessages()) .map(LogEntriesEntity.Message::getId) .doesNotContain(firstId); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntriesSearch(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntriesSearch(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logs = arangoDB.async().getLogEntries(null).get(); - final LogEntriesEntity logsSearch = arangoDB.async().getLogEntries(new LogOptions().search(TEST_DB)).get(); + final LogEntriesEntity logs = arangoDB.getLogEntries(null).get(); + final LogEntriesEntity logsSearch = arangoDB.getLogEntries(new LogOptions().search(TEST_DB)).get(); assertThat(logs.getTotal()).isGreaterThan(logsSearch.getTotal()); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntriesSortAsc(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntriesSortAsc(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logs = arangoDB.async().getLogEntries(new LogOptions().sort(SortOrder.asc)).get(); + final LogEntriesEntity logs = arangoDB.getLogEntries(new LogOptions().sort(SortOrder.asc)).get(); long lastId = -1; List ids = logs.getMessages().stream() .map(LogEntriesEntity.Message::getId) @@ -475,10 +482,10 @@ void getLogEntriesSortAsc(ArangoDB arangoDB) throws ExecutionException, Interrup } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogEntriesSortDesc(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogEntriesSortDesc(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 8)); - final LogEntriesEntity logs = arangoDB.async().getLogEntries(new LogOptions().sort(SortOrder.desc)).get(); + final LogEntriesEntity logs = arangoDB.getLogEntries(new LogOptions().sort(SortOrder.desc)).get(); long lastId = Long.MAX_VALUE; List ids = logs.getMessages().stream() .map(LogEntriesEntity.Message::getId) @@ -490,70 +497,70 @@ void getLogEntriesSortDesc(ArangoDB arangoDB) throws ExecutionException, Interru } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getLogLevel(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getLogLevel(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 7)); // it fails in 3.6 active-failover (BTS-362) - final LogLevelEntity logLevel = arangoDB.async().getLogLevel().get(); + final LogLevelEntity logLevel = arangoDB.getLogLevel().get(); assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.INFO); } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void setLogLevel(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void setLogLevel(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 7)); // it fails in 3.6 active-failover (BTS-362) final LogLevelEntity entity = new LogLevelEntity(); try { entity.setAgency(LogLevelEntity.LogLevel.ERROR); - final LogLevelEntity logLevel = arangoDB.async().setLogLevel(entity).get(); + final LogLevelEntity logLevel = arangoDB.setLogLevel(entity).get(); assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); } finally { entity.setAgency(LogLevelEntity.LogLevel.INFO); - arangoDB.async().setLogLevel(entity); + arangoDB.setLogLevel(entity); } } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void setAllLogLevel(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void setAllLogLevel(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 9)); final LogLevelEntity entity = new LogLevelEntity(); try { entity.setAll(LogLevelEntity.LogLevel.ERROR); - final LogLevelEntity logLevel = arangoDB.async().setLogLevel(entity).get(); + final LogLevelEntity logLevel = arangoDB.setLogLevel(entity).get(); assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); assertThat(logLevel.getQueries()).isEqualTo(LogLevelEntity.LogLevel.ERROR); - LogLevelEntity retrievedLevels = arangoDB.async().getLogLevel().get(); + LogLevelEntity retrievedLevels = arangoDB.getLogLevel().get(); assertThat(retrievedLevels.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); } finally { entity.setAll(LogLevelEntity.LogLevel.INFO); - arangoDB.async().setLogLevel(entity); + arangoDB.setLogLevel(entity); } } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void logLevelWithServerId(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void logLevelWithServerId(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 10)); assumeTrue(isCluster()); - String serverId = arangoDB.async().getServerId().get(); + String serverId = arangoDB.getServerId().get(); LogLevelOptions options = new LogLevelOptions().serverId(serverId); final LogLevelEntity entity = new LogLevelEntity(); try { entity.setGraphs(LogLevelEntity.LogLevel.ERROR); - final LogLevelEntity logLevel = arangoDB.async().setLogLevel(entity, options).get(); + final LogLevelEntity logLevel = arangoDB.setLogLevel(entity, options).get(); assertThat(logLevel.getGraphs()).isEqualTo(LogLevelEntity.LogLevel.ERROR); - assertThat(arangoDB.async().getLogLevel(options).get().getGraphs()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + assertThat(arangoDB.getLogLevel(options).get().getGraphs()).isEqualTo(LogLevelEntity.LogLevel.ERROR); } finally { entity.setGraphs(LogLevelEntity.LogLevel.INFO); - arangoDB.async().setLogLevel(entity); + arangoDB.setLogLevel(entity); } } @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void getQueryOptimizerRules(ArangoDB arangoDB) throws ExecutionException, InterruptedException { + @MethodSource("asyncArangos") + void getQueryOptimizerRules(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 10)); - final Collection rules = arangoDB.async().getQueryOptimizerRules().get(); + final Collection rules = arangoDB.getQueryOptimizerRules().get(); assertThat(rules).isNotEmpty(); for (QueryOptimizerRule rule : rules) { assertThat(rule).isNotNull(); @@ -568,61 +575,55 @@ void getQueryOptimizerRules(ArangoDB arangoDB) throws ExecutionException, Interr } } -// @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") -// void arangoDBException(ArangoDB arangoDB) { -// Throwable thrown = catchThrowable(() -> arangoDB.async().db("no").getInfo()); -// assertThat(thrown).isInstanceOf(ArangoDBException.class); -// ArangoDBException e = (ArangoDBException) thrown; -// assertThat(e.getResponseCode()).isEqualTo(404); -// assertThat(e.getErrorNum()).isEqualTo(1228); -// } - -// @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") -// void fallbackHost() { -// final ArangoDB arangoDB = new ArangoDB.Builder() -// .loadProperties(config) -// .host("not-accessible", 8529).host("127.0.0.1", 8529).build(); -// final ArangoDBVersion version = arangoDB.async().getVersion(); -// assertThat(version).isNotNull(); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void arangoDBException(ArangoDBAsync arangoDB) { + Throwable thrown = catchThrowable(() -> arangoDB.db("no").getInfo().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(404); + assertThat(e.getErrorNum()).isEqualTo(1228); + } -// @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") -// void loadproperties() { -// Throwable thrown = catchThrowable(() -> new ArangoDB.Builder() -// .loadProperties(ConfigUtils.loadConfig("arangodb-bad.properties")) -// ); -// assertThat(thrown).isInstanceOf(IllegalArgumentException.class); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void fallbackHost() throws ExecutionException, InterruptedException { + final ArangoDBAsync arangoDB = new ArangoDB.Builder() + .loadProperties(config) + .host("not-accessible", 8529).host("127.0.0.1", 8529) + .build() + .async(); + final ArangoDBVersion version = arangoDB.getVersion().get(); + assertThat(version).isNotNull(); + } -// @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") -// void loadpropertiesWithPrefix() { -// ArangoDB adb = new arangoDB.async().Builder() -// .loadProperties(ConfigUtils.loadConfig("arangodb-with-prefix.properties", "adb")) -// .build(); -// adb.getVersion(); -// adb.shutdown(); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void loadpropertiesWithPrefix() throws ExecutionException, InterruptedException { + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ConfigUtils.loadConfig("arangodb-with-prefix.properties", "adb")) + .build() + .async(); + adb.getVersion().get(); + adb.shutdown(); + } -// @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") -// void accessMultipleDatabases(ArangoDB arangoDB) { -// final ArangoDBVersion version1 = arangoDB.async().db(DB1).getVersion(); -// assertThat(version1).isNotNull(); -// final ArangoDBVersion version2 = arangoDB.async().db(DB2).getVersion(); -// assertThat(version2).isNotNull(); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void accessMultipleDatabases(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + final ArangoDBVersion version1 = arangoDB.db(DB1).getVersion().get(); + assertThat(version1).isNotNull(); + final ArangoDBVersion version2 = arangoDB.db(DB2).getVersion().get(); + assertThat(version2).isNotNull(); + } // @ParameterizedTest(name = "{index}") -// @MethodSource("arangos") +// @MethodSource("asyncArangos") // @Disabled("Manual execution only") -// void queueTime(ArangoDB arangoDB) throws InterruptedException, ExecutionException { +// void queueTime(ArangoDBAsync arangoDB) throws InterruptedException, ExecutionException { // List> futures = IntStream.range(0, 80) // .mapToObj(i -> CompletableFuture.runAsync( -// () -> arangoDB.async().db().query("RETURN SLEEP(1)", Void.class), +// () -> arangoDB.db().query("RETURN SLEEP(1)", Void.class), // Executors.newFixedThreadPool(80)) // ) // .collect(Collectors.toList()); @@ -630,7 +631,7 @@ void getQueryOptimizerRules(ArangoDB arangoDB) throws ExecutionException, Interr // f.get(); // } // -// QueueTimeMetrics qt = arangoDB.async().metrics().getQueueTime(); +// QueueTimeMetrics qt = arangoDB.metrics().getQueueTime(); // double avg = qt.getAvg(); // QueueTimeSample[] values = qt.getValues(); // if (isAtLeastVersion(3, 9)) { diff --git a/driver/src/test/java/com/arangodb/BaseJunit5.java b/driver/src/test/java/com/arangodb/BaseJunit5.java index 91ac3cd88..e7a4f7740 100644 --- a/driver/src/test/java/com/arangodb/BaseJunit5.java +++ b/driver/src/test/java/com/arangodb/BaseJunit5.java @@ -38,6 +38,10 @@ protected static Stream arangos() { return adbs.stream().map(Arguments::of); } + protected static Stream asyncArangos() { + return adbs.stream().map(ArangoDB::async).map(Arguments::of); + } + protected static Stream dbs() { return dbsStream().map(Arguments::of); } From 353962518a5b5f1ab070ff61b5bdf770ca2d0322 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 12 Oct 2023 13:55:00 +0200 Subject: [PATCH 18/62] ArangoDatabaseAsyncTest --- .../internal/ArangoDatabaseAsyncImpl.java | 30 +- .../com/arangodb/ArangoDatabaseAsyncTest.java | 1530 +++++++++++++++++ .../test/java/com/arangodb/BaseJunit5.java | 19 +- 3 files changed, 1561 insertions(+), 18 deletions(-) create mode 100644 driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index b07acaeaa..893c04b71 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -33,6 +33,7 @@ import java.util.Collection; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import static com.arangodb.internal.serde.SerdeUtils.constructListType; @@ -66,21 +67,20 @@ public CompletableFuture getEngine() { @Override public CompletableFuture exists() { - return getInfo() - .handle((result, ex) -> { - if (result != null) { - return true; - } - - if (ex instanceof ArangoDBException) { - ArangoDBException e = (ArangoDBException) ex.getCause(); - if (ArangoErrors.ERROR_ARANGO_DATABASE_NOT_FOUND.equals(e.getErrorNum())) { - return false; - } - } - - throw ArangoDBException.wrap(ex); - }); + return getInfo().handle((result, ex) -> { + if (result != null) { + return true; + } + + if (ex instanceof CompletionException && ex.getCause() instanceof ArangoDBException) { + ArangoDBException e = (ArangoDBException) ex.getCause(); + if (ArangoErrors.ERROR_ARANGO_DATABASE_NOT_FOUND.equals(e.getErrorNum())) { + return false; + } + } + + throw new CompletionException(ex); + }); } @Override diff --git a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java new file mode 100644 index 000000000..13166c82f --- /dev/null +++ b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java @@ -0,0 +1,1530 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.entity.AqlExecutionExplainEntity.ExecutionPlan; +import com.arangodb.entity.QueryCachePropertiesEntity.CacheMode; +import com.arangodb.model.*; +import com.arangodb.util.MapBuilder; +import com.arangodb.util.RawJson; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoDatabaseAsyncTest extends BaseJunit5 { + + private static final String CNAME1 = "ArangoDatabaseTest_collection_1"; + private static final String CNAME2 = "ArangoDatabaseTest_collection_2"; + private static final String ENAMES = "ArangoDatabaseTest_edge_collection"; + + @BeforeAll + static void init() { + BaseJunit5.initDB(); + BaseJunit5.initCollections(CNAME1, CNAME2); + BaseJunit5.initEdgeCollections(ENAMES); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getVersion(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final ArangoDBVersion version = db.getVersion().get(); + assertThat(version).isNotNull(); + assertThat(version.getServer()).isNotNull(); + assertThat(version.getVersion()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getEngine(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final ArangoDBEngine engine = db.getEngine().get(); + assertThat(engine).isNotNull(); + assertThat(engine.getName()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void exists(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + assertThat(arangoDB.db(TEST_DB).exists().get()).isTrue(); + assertThat(arangoDB.db("no").exists().get()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getAccessibleDatabases(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Collection dbs = db.getAccessibleDatabases().get(); + assertThat(dbs).contains("_system"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + String name = rndName(); + final CollectionEntity result = db.createCollection(name, null).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithNotNormalizedName(ArangoDatabaseAsync db) { + assumeTrue(supportsExtendedNames()); + final String colName = "testCol-\u006E\u0303\u00f1"; + + Throwable thrown = catchThrowable(() -> db.createCollection(colName)); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .hasMessageContaining("normalized") + .extracting(it -> ((ArangoDBException) it).getResponseCode()).isEqualTo(400); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithReplicationFactor(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + String name = rndName(); + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions().replicationFactor(2)).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); +// CollectionPropertiesEntity props = db.collection(name).getProperties(); +// assertThat(props.getReplicationFactor().get()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithWriteConcern(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isCluster()); + + String name = rndName(); + final CollectionEntity result = db.createCollection(name, + new CollectionCreateOptions().replicationFactor(2).writeConcern(2)).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); +// CollectionPropertiesEntity props = db.collection(name).getProperties(); +// assertThat(props.getReplicationFactor().get()).isEqualTo(2); +// assertThat(props.getWriteConcern()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createSatelliteCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + + String name = rndName(); + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions().replicationFactor(ReplicationFactor.ofSatellite())).get(); + + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); +// CollectionPropertiesEntity props = db.collection(name).getProperties(); +// assertThat(props.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithNumberOfShards(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + String name = rndName(); + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions().numberOfShards(2)).get(); + + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); +// CollectionPropertiesEntity props = db.collection(name).getProperties(); +// assertThat(props.getNumberOfShards()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithShardingStrategys(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + assumeTrue(isCluster()); + + String name = rndName(); + final CollectionEntity result = db.createCollection(name, new CollectionCreateOptions() + .shardingStrategy(ShardingStrategy.COMMUNITY_COMPAT.getInternalName())).get(); + + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); +// CollectionPropertiesEntity props = db.collection(name).getProperties(); +// assertThat(props.getShardingStrategy()).isEqualTo(ShardingStrategy.COMMUNITY_COMPAT.getInternalName()); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void createCollectionWithSmartJoinAttribute(ArangoDatabaseAsync db) { +// assumeTrue(isAtLeastVersion(3, 5)); +// assumeTrue(isEnterprise()); +// assumeTrue(isCluster()); +// +// String fooName = rndName(); +// db.collection(fooName).create(); +// +// String name = rndName(); +// final CollectionEntity result = db.createCollection(name, +// new CollectionCreateOptions().smartJoinAttribute("test123").distributeShardsLike(fooName).shardKeys("_key:")); +// assertThat(result).isNotNull(); +// assertThat(result.getId()).isNotNull(); +// assertThat(db.collection(name).getProperties().getSmartJoinAttribute()).isEqualTo("test123"); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithSmartJoinAttributeWrong(ArangoDatabaseAsync db) { + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + + String name = rndName(); + + Throwable thrown = catchThrowable(() -> db.createCollection(name, new CollectionCreateOptions().smartJoinAttribute("test123")).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) thrown).getErrorNum()).isEqualTo(4006); + assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(400); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithNumberOfShardsAndShardKey(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + + String name = rndName(); + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions().numberOfShards(2).shardKeys("a")).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); +// final CollectionPropertiesEntity properties = db.collection(name).getProperties(); +// assertThat(properties.getNumberOfShards()).isEqualTo(2); +// assertThat(properties.getShardKeys()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithNumberOfShardsAndShardKeys(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + String name = rndName(); + final CollectionEntity result = db.createCollection(name, + new CollectionCreateOptions().numberOfShards(2).shardKeys("a", "b")).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); +// final CollectionPropertiesEntity properties = db.collection(name).getProperties(); +// assertThat(properties.getNumberOfShards()).isEqualTo(2); +// assertThat(properties.getShardKeys()).hasSize(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithDistributeShardsLike(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + + final Integer numberOfShards = 3; + + String name1 = rndName(); + String name2 = rndName(); + db.createCollection(name1, new CollectionCreateOptions().numberOfShards(numberOfShards)).get(); + db.createCollection(name2, new CollectionCreateOptions().distributeShardsLike(name1)).get(); + +// assertThat(db.collection(name1).getProperties().getNumberOfShards()).isEqualTo(numberOfShards); +// assertThat(db.collection(name2).getProperties().getNumberOfShards()).isEqualTo(numberOfShards); + } + + private void createCollectionWithKeyType(ArangoDatabaseAsync db, KeyType keyType) throws ExecutionException, InterruptedException { + String name = rndName(); + db.createCollection(name, new CollectionCreateOptions().keyOptions( + false, + keyType, + null, + null + )).get(); +// assertThat(db.collection(name).getProperties().getKeyOptions().getType()).isEqualTo(keyType); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithKeyTypeAutoincrement(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + createCollectionWithKeyType(db, KeyType.autoincrement); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithKeyTypePadded(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + createCollectionWithKeyType(db, KeyType.padded); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithKeyTypeTraditional(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + createCollectionWithKeyType(db, KeyType.traditional); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithKeyTypeUuid(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + createCollectionWithKeyType(db, KeyType.uuid); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithJsonSchema(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + String name = rndName(); + String rule = ("{ " + + " \"properties\": {" + + " \"number\": {" + + " \"type\": \"number\"" + + " }" + + " }" + + " }") + .replaceAll("\\s", ""); + String message = "The document has problems!"; + + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions() + .schema( + new CollectionSchema() + .setLevel(CollectionSchema.Level.NEW) + .setMessage(message) + .setRule(rule) + ) + ).get(); + assertThat(result.getSchema().getLevel()).isEqualTo(CollectionSchema.Level.NEW); + assertThat(result.getSchema().getRule()).isEqualTo(rule); + assertThat(result.getSchema().getMessage()).isEqualTo(message); + +// CollectionPropertiesEntity props = db.collection(name).getProperties(); +// assertThat(props.getSchema().getLevel()).isEqualTo(CollectionSchema.Level.NEW); +// assertThat(props.getSchema().getRule()).isEqualTo(rule); +// assertThat(props.getSchema().getMessage()).isEqualTo(message); + +// BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); +// doc.addAttribute("number", 33); +// db.collection(name).insertDocument(doc); + +// BaseDocument wrongDoc = new BaseDocument(UUID.randomUUID().toString()); +// wrongDoc.addAttribute("number", "notANumber"); +// Throwable thrown = catchThrowable(() -> db.collection(name).insertDocument(wrongDoc)); +// assertThat(thrown).isInstanceOf(ArangoDBException.class); +// ArangoDBException e = (ArangoDBException) thrown; + +// assertThat(e).hasMessageContaining(message); +// assertThat(e.getResponseCode()).isEqualTo(400); +// assertThat(e.getErrorNum()).isEqualTo(1620); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithComputedFields(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String cName = rndName(); + ComputedValue cv = new ComputedValue() + .name("foo") + .expression("RETURN 11") + .overwrite(false) + .computeOn(ComputedValue.ComputeOn.insert) + .keepNull(false) + .failOnWarning(true); + + final CollectionEntity result = db.createCollection(cName, new CollectionCreateOptions().computedValues(cv)).get(); + + assertThat(result).isNotNull(); + assertThat(result.getComputedValues()) + .hasSize(1) + .contains(cv); + + ComputedValue cv2 = new ComputedValue() + .name("bar") + .expression("RETURN 22") + .overwrite(true) + .computeOn(ComputedValue.ComputeOn.update, ComputedValue.ComputeOn.replace) + .keepNull(true) + .failOnWarning(false); + +// db.collection(cName).changeProperties(new CollectionPropertiesOptions().computedValues(cv2)); +// +// CollectionPropertiesEntity props = db.collection(cName).getProperties(); +// assertThat(props.getComputedValues()) +// .hasSize(1) +// .contains(cv2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void deleteCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + String name = rndName(); + db.createCollection(name, null).get(); +// db.collection(name).drop(); +// Throwable thrown = catchThrowable(() -> db.collection(name).getInfo()); +// assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void deleteSystemCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { +// final String name = "_system_test"; +// db.createCollection(name, new CollectionCreateOptions().isSystem(true)).get(); +// db.collection(name).drop(true); +// Throwable thrown = catchThrowable(() -> db.collection(name).getInfo()); +// assertThat(thrown) +// .isInstanceOf(ArangoDBException.class) +// .extracting(it -> ((ArangoDBException) it).getResponseCode()) +// .isEqualTo(404); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void deleteSystemCollectionFail(ArangoDatabaseAsync db) { +// final String name = "_system_test"; +// ArangoCollection collection = db.collection(name); +// if (collection.exists()) +// collection.drop(true); +// +// db.createCollection(name, new CollectionCreateOptions().isSystem(true)); +// try { +// collection.drop(); +// fail(); +// } catch (final ArangoDBException e) { +// assertThat(e.getResponseCode()).isEqualTo(403); +// } +// collection.drop(true); +// try { +// collection.getInfo(); +// fail(); +// } catch (final ArangoDBException e) { +// assertThat(e.getResponseCode()).isEqualTo(404); +// } +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void getIndex(ArangoDatabaseAsync db) { +// final Collection fields = Collections.singletonList("field-" + rnd()); +// final IndexEntity createResult = db.collection(CNAME1).ensurePersistentIndex(fields, null); +// final IndexEntity readResult = db.getIndex(createResult.getId()); +// assertThat(readResult.getId()).isEqualTo(createResult.getId()); +// assertThat(readResult.getType()).isEqualTo(createResult.getType()); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void deleteIndex(ArangoDatabaseAsync db) { +// final Collection fields = Collections.singletonList("field-" + rnd()); +// final IndexEntity createResult = db.collection(CNAME1).ensurePersistentIndex(fields, null); +// final String id = db.deleteIndex(createResult.getId()); +// assertThat(id).isEqualTo(createResult.getId()); +// try { +// db.getIndex(id); +// fail(); +// } catch (final ArangoDBException e) { +// assertThat(e.getResponseCode()).isEqualTo(404); +// } +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getCollections(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Collection collections = db.getCollections(null).get(); + long count = collections.stream().map(CollectionEntity::getName).filter(it -> it.equals(CNAME1)).count(); + assertThat(count).isEqualTo(1L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getCollectionsExcludeSystem(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final CollectionsReadOptions options = new CollectionsReadOptions().excludeSystem(true); + final Collection nonSystemCollections = db.getCollections(options).get(); + final Collection allCollections = db.getCollections(null).get(); + assertThat(allCollections).hasSizeGreaterThan(nonSystemCollections.size()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void grantAccess(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null).get(); + arangoDB.db(TEST_DB).grantAccess(user).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void grantAccessRW(ArangoDBAsync arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).grantAccess(user, Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void grantAccessRO(ArangoDBAsync arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).grantAccess(user, Permissions.RO); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void grantAccessNONE(ArangoDBAsync arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).grantAccess(user, Permissions.NONE); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void grantAccessUserNotFound(ArangoDatabaseAsync db) { + String user = "user-" + rnd(); + Throwable thrown = catchThrowable(() -> db.grantAccess(user, Permissions.RW).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void revokeAccess(ArangoDBAsync arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).revokeAccess(user); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void revokeAccessUserNotFound(ArangoDatabaseAsync db) { + String user = "user-" + rnd(); + Throwable thrown = catchThrowable(() -> db.revokeAccess(user).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void resetAccess(ArangoDBAsync arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).resetAccess(user); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void resetAccessUserNotFound(ArangoDatabaseAsync db) { + String user = "user-" + rnd(); + Throwable thrown = catchThrowable(() -> db.resetAccess(user).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void grantDefaultCollectionAccess(ArangoDBAsync arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234"); + arangoDB.db(TEST_DB).grantDefaultCollectionAccess(user, Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getPermissions(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assertThat(db.getPermissions("root").get()).isEqualTo(Permissions.RW); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void query(ArangoDatabaseAsync db) { +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// final ArangoCursor cursor = db.query("for i in " + CNAME1 + " return i._id", String.class); +// assertThat((Object) cursor).isNotNull(); +// for (int i = 0; i < 10; i++, cursor.next()) { +// assertThat((Iterator) cursor).hasNext(); +// } +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithNullBindVar(ArangoDatabaseAsync db) { +// final ArangoCursor cursor = db.query("return @foo", Object.class, Collections.singletonMap("foo", null)); +// assertThat(cursor.hasNext()).isTrue(); +// assertThat(cursor.next()).isNull(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryForEach(ArangoDatabaseAsync db) { +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// final ArangoCursor cursor = db.query("for i in " + CNAME1 + " return i._id", String.class); +// assertThat((Object) cursor).isNotNull(); +// +// int i = 0; +// while (cursor.hasNext()) { +// cursor.next(); +// i++; +// } +// assertThat(i).isGreaterThanOrEqualTo(10); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithCount(ArangoDatabaseAsync db) { +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// +// final ArangoCursor cursor = db +// .query("for i in " + CNAME1 + " Limit 6 return i._id", String.class, new AqlQueryOptions().count(true)); +// assertThat((Object) cursor).isNotNull(); +// for (int i = 1; i <= 6; i++, cursor.next()) { +// assertThat(cursor.hasNext()).isTrue(); +// } +// assertThat(cursor.getCount()).isEqualTo(6); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithLimitAndFullCount(ArangoDatabaseAsync db) { +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// +// final ArangoCursor cursor = db +// .query("for i in " + CNAME1 + " Limit 5 return i._id", String.class, new AqlQueryOptions().fullCount(true)); +// assertThat((Object) cursor).isNotNull(); +// for (int i = 0; i < 5; i++, cursor.next()) { +// assertThat((Iterator) cursor).hasNext(); +// } +// assertThat(cursor.getStats()).isNotNull(); +// assertThat(cursor.getStats().getExecutionTime()).isPositive(); +// assertThat((cursor.getStats().getFullCount())).isGreaterThanOrEqualTo(10); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryStats(ArangoDatabaseAsync db) { +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// +// final ArangoCursor cursor = db.query("for i in " + CNAME1 + " return i", Object.class); +// assertThat((Object) cursor).isNotNull(); +// for (int i = 0; i < 5; i++, cursor.next()) { +// assertThat((Iterator) cursor).hasNext(); +// } +// assertThat(cursor.getStats()).isNotNull(); +// assertThat(cursor.getStats().getWritesExecuted()).isNotNull(); +// assertThat(cursor.getStats().getWritesIgnored()).isNotNull(); +// assertThat(cursor.getStats().getScannedFull()).isNotNull(); +// assertThat(cursor.getStats().getScannedIndex()).isNotNull(); +// assertThat(cursor.getStats().getFiltered()).isNotNull(); +// assertThat(cursor.getStats().getExecutionTime()).isNotNull(); +// assertThat(cursor.getStats().getPeakMemoryUsage()).isNotNull(); +// if (isAtLeastVersion(3, 10)) { +// assertThat(cursor.getStats().getCursorsCreated()).isNotNull(); +// assertThat(cursor.getStats().getCursorsRearmed()).isNotNull(); +// assertThat(cursor.getStats().getCacheHits()).isNotNull(); +// assertThat(cursor.getStats().getCacheMisses()).isNotNull(); +// } +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithBatchSize(ArangoDatabaseAsync db) { +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// +// final ArangoCursor cursor = db +// .query("for i in " + CNAME1 + " return i._id", String.class, new AqlQueryOptions().batchSize(5).count(true)); +// +// assertThat((Object) cursor).isNotNull(); +// for (int i = 0; i < 10; i++, cursor.next()) { +// assertThat((Iterator) cursor).hasNext(); +// } +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryIterateWithBatchSize(ArangoDatabaseAsync db) { +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// +// final ArangoCursor cursor = db +// .query("for i in " + CNAME1 + " return i._id", String.class, new AqlQueryOptions().batchSize(5).count(true)); +// +// assertThat((Object) cursor).isNotNull(); +// final AtomicInteger i = new AtomicInteger(0); +// for (; cursor.hasNext(); cursor.next()) { +// i.incrementAndGet(); +// } +// assertThat(i.get()).isGreaterThanOrEqualTo(10); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithTTL(ArangoDatabaseAsync db) throws InterruptedException { +// // set TTL to 1 seconds and get the second batch after 2 seconds! +// final int ttl = 1; +// final int wait = 2; +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// +// final ArangoCursor cursor = db +// .query("for i in " + CNAME1 + " return i._id", String.class, new AqlQueryOptions().batchSize(5).ttl(ttl)); +// +// assertThat((Iterable) cursor).isNotNull(); +// +// try { +// for (int i = 0; i < 10; i++, cursor.next()) { +// assertThat(cursor.hasNext()).isTrue(); +// if (i == 1) { +// Thread.sleep(wait * 1000); +// } +// } +// fail("this should fail"); +// } catch (final ArangoDBException ex) { +// assertThat(ex.getMessage()).isEqualTo("Response: 404, Error: 1600 - cursor not found"); +// } +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void changeQueryCache(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + QueryCachePropertiesEntity properties = db.getQueryCacheProperties().get(); + assertThat(properties).isNotNull(); + assertThat(properties.getMode()).isEqualTo(CacheMode.off); + assertThat(properties.getMaxResults()).isPositive(); + + properties.setMode(CacheMode.on); + properties = db.setQueryCacheProperties(properties).get(); + assertThat(properties).isNotNull(); + assertThat(properties.getMode()).isEqualTo(CacheMode.on); + + properties = db.getQueryCacheProperties().get(); + assertThat(properties.getMode()).isEqualTo(CacheMode.on); + + final QueryCachePropertiesEntity properties2 = new QueryCachePropertiesEntity(); + properties2.setMode(CacheMode.off); + db.setQueryCacheProperties(properties2); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithCache(ArangoDatabaseAsync db) { +// assumeTrue(isSingleServer()); +// for (int i = 0; i < 10; i++) { +// db.collection(CNAME1).insertDocument(new BaseDocument(), null); +// } +// +// final QueryCachePropertiesEntity properties = new QueryCachePropertiesEntity(); +// properties.setMode(CacheMode.on); +// db.setQueryCacheProperties(properties); +// +// final ArangoCursor cursor = db +// .query("FOR t IN " + CNAME1 + " FILTER t.age >= 10 SORT t.age RETURN t._id", String.class, +// new AqlQueryOptions().cache(true)); +// +// assertThat((Object) cursor).isNotNull(); +// assertThat(cursor.isCached()).isFalse(); +// +// final ArangoCursor cachedCursor = db +// .query("FOR t IN " + CNAME1 + " FILTER t.age >= 10 SORT t.age RETURN t._id", String.class, +// new AqlQueryOptions().cache(true)); +// +// assertThat((Object) cachedCursor).isNotNull(); +// assertThat(cachedCursor.isCached()).isTrue(); +// +// final QueryCachePropertiesEntity properties2 = new QueryCachePropertiesEntity(); +// properties2.setMode(CacheMode.off); +// db.setQueryCacheProperties(properties2); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithMemoryLimit(ArangoDatabaseAsync db) { +// Throwable thrown = catchThrowable(() -> db.query("RETURN 1..100000", String.class, +// new AqlQueryOptions().memoryLimit(32 * 1024L))); +// assertThat(thrown).isInstanceOf(ArangoDBException.class); +// assertThat(((ArangoDBException) thrown).getErrorNum()).isEqualTo(32); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithFailOnWarningTrue(ArangoDatabaseAsync db) { +// Throwable thrown = catchThrowable(() -> db.query("RETURN 1 / 0", String.class, +// new AqlQueryOptions().failOnWarning(true))); +// assertThat(thrown).isInstanceOf(ArangoDBException.class); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithFailOnWarningFalse(ArangoDatabaseAsync db) { +// final ArangoCursor cursor = db +// .query("RETURN 1 / 0", String.class, new AqlQueryOptions().failOnWarning(false)); +// assertThat(cursor.next()).isNull(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithTimeout(ArangoDatabaseAsync db) { +// assumeTrue(isAtLeastVersion(3, 6)); +// Throwable thrown = catchThrowable(() -> db.query("RETURN SLEEP(1)", String.class, +// new AqlQueryOptions().maxRuntime(0.1)).next()); +// assertThat(thrown).isInstanceOf(ArangoDBException.class); +// assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(410); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithMaxWarningCount(ArangoDatabaseAsync db) { +// final ArangoCursor cursorWithWarnings = db +// .query("RETURN 1 / 0", String.class, new AqlQueryOptions()); +// assertThat(cursorWithWarnings.getWarnings()).hasSize(1); +// final ArangoCursor cursorWithLimitedWarnings = db +// .query("RETURN 1 / 0", String.class, new AqlQueryOptions().maxWarningCount(0L)); +// final Collection warnings = cursorWithLimitedWarnings.getWarnings(); +// assertThat(warnings).isNullOrEmpty(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryCursor(ArangoDatabaseAsync db) { +// ArangoCursor cursor = db.query("for i in 1..4 return i", Integer.class, +// new AqlQueryOptions().batchSize(1)); +// List result = new ArrayList<>(); +// result.add(cursor.next()); +// result.add(cursor.next()); +// ArangoCursor cursor2 = db.cursor(cursor.getId(), Integer.class); +// result.add(cursor2.next()); +// result.add(cursor2.next()); +// assertThat(cursor2.hasNext()).isFalse(); +// assertThat(result).containsExactly(1, 2, 3, 4); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryCursorRetry(ArangoDatabaseAsync db) throws IOException { +// assumeTrue(isAtLeastVersion(3, 11)); +// ArangoCursor cursor = db.query("for i in 1..4 return i", Integer.class, +// new AqlQueryOptions().batchSize(1).allowRetry(true)); +// List result = new ArrayList<>(); +// result.add(cursor.next()); +// result.add(cursor.next()); +// ArangoCursor cursor2 = db.cursor(cursor.getId(), Integer.class, cursor.getNextBatchId()); +// result.add(cursor2.next()); +// result.add(cursor2.next()); +// cursor2.close(); +// assertThat(cursor2.hasNext()).isFalse(); +// assertThat(result).containsExactly(1, 2, 3, 4); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void changeQueryTrackingProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + try { + QueryTrackingPropertiesEntity properties = db.getQueryTrackingProperties().get(); + assertThat(properties).isNotNull(); + assertThat(properties.getEnabled()).isTrue(); + assertThat(properties.getTrackSlowQueries()).isTrue(); + assertThat(properties.getMaxQueryStringLength()).isPositive(); + assertThat(properties.getMaxSlowQueries()).isPositive(); + assertThat(properties.getSlowQueryThreshold()).isPositive(); + properties.setEnabled(false); + properties = db.setQueryTrackingProperties(properties).get(); + assertThat(properties).isNotNull(); + assertThat(properties.getEnabled()).isFalse(); + properties = db.getQueryTrackingProperties().get(); + assertThat(properties.getEnabled()).isFalse(); + } finally { + final QueryTrackingPropertiesEntity properties = new QueryTrackingPropertiesEntity(); + properties.setEnabled(true); + db.setQueryTrackingProperties(properties).get(); + } + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithBindVars(ArangoDatabaseAsync db) { +// for (int i = 0; i < 10; i++) { +// final BaseDocument baseDocument = new BaseDocument(UUID.randomUUID().toString()); +// baseDocument.addAttribute("age", 20 + i); +// db.collection(CNAME1).insertDocument(baseDocument, null); +// } +// final Map bindVars = new HashMap<>(); +// bindVars.put("@coll", CNAME1); +// bindVars.put("age", 25); +// +// final ArangoCursor cursor = db +// .query("FOR t IN @@coll FILTER t.age >= @age SORT t.age RETURN t._id", String.class, bindVars); +// +// assertThat((Object) cursor).isNotNull(); +// +// for (int i = 0; i < 5; i++, cursor.next()) { +// assertThat((Iterator) cursor).hasNext(); +// } +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithRawBindVars(ArangoDatabaseAsync db) { +// final Map bindVars = new HashMap<>(); +// bindVars.put("foo", RawJson.of("\"fooValue\"")); +// bindVars.put("bar", RawBytes.of(db.getSerde().serializeUserData(11))); +// +// final JsonNode res = db.query("RETURN {foo: @foo, bar: @bar}", JsonNode.class, bindVars).next(); +// +// assertThat(res.get("foo").textValue()).isEqualTo("fooValue"); +// assertThat(res.get("bar").intValue()).isEqualTo(11); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncArangos") +// void queryWithWarning(ArangoDBAsync arangoDB) { +// final ArangoCursor cursor = arangoDB.db().query("return 1/0", String.class); +// +// assertThat((Object) cursor).isNotNull(); +// assertThat(cursor.getWarnings()).isNotNull(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryStream(ArangoDatabaseAsync db) { +// final ArangoCursor cursor = db +// .query("FOR i IN 1..2 RETURN i", Void.class, new AqlQueryOptions().stream(true).count(true)); +// assertThat((Object) cursor).isNotNull(); +// assertThat(cursor.getCount()).isNull(); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryForceOneShardAttributeValue(ArangoDatabaseAsync db) { +// assumeTrue(isAtLeastVersion(3, 10)); +// assumeTrue(isCluster()); +// assumeTrue(isEnterprise()); +// +// String cname = "forceOneShardAttr-" + UUID.randomUUID(); +// db.createCollection(cname, new CollectionCreateOptions() +// .shardKeys("foo") +// .numberOfShards(3)); +// ArangoCollection col = db.collection(cname); +// BaseDocument doc = new BaseDocument(); +// doc.addAttribute("foo", "bar"); +// col.insertDocument(doc); +// +// ArangoCursor c1 = db +// .query("FOR d IN @@c RETURN d", BaseDocument.class, Collections.singletonMap("@c", cname), +// new AqlQueryOptions().forceOneShardAttributeValue("bar")); +// assertThat(c1.hasNext()).isTrue(); +// assertThat(c1.next().getAttribute("foo")).isEqualTo("bar"); +// +// ArangoCursor c2 = db +// .query("FOR d IN @@c RETURN d", BaseDocument.class, Collections.singletonMap("@c", cname), +// new AqlQueryOptions().forceOneShardAttributeValue("ooo")); +// assertThat(c2.hasNext()).isFalse(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncArangos") +// void queryClose(ArangoDBAsync arangoDB) throws IOException { +// final ArangoCursor cursor = arangoDB.db() +// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().batchSize(1)); +// cursor.close(); +// AtomicInteger count = new AtomicInteger(); +// Throwable thrown = catchThrowable(() -> { +// while (cursor.hasNext()) { +// cursor.next(); +// count.incrementAndGet(); +// } +// }); +// +// assertThat(thrown).isInstanceOf(ArangoDBException.class); +// assertThat(count).hasValue(1); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryNoResults(ArangoDatabaseAsync db) throws IOException { +// final ArangoCursor cursor = db +// .query("FOR i IN @@col RETURN i", BaseDocument.class, new MapBuilder().put("@col", CNAME1).get()); +// cursor.close(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryWithNullBindParam(ArangoDatabaseAsync db) throws IOException { +// final ArangoCursor cursor = db.query("FOR i IN @@col FILTER i.test == @test RETURN i", +// BaseDocument.class, new MapBuilder().put("@col", CNAME1).put("test", null).get()); +// cursor.close(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void queryAllowDirtyRead(ArangoDatabaseAsync db) throws IOException { +// final ArangoCursor cursor = db.query("FOR i IN @@col FILTER i.test == @test RETURN i", +// BaseDocument.class, new MapBuilder().put("@col", CNAME1).put("test", null).get(), +// new AqlQueryOptions().allowDirtyRead(true)); +// if (isAtLeastVersion(3, 10)) { +// assertThat(cursor.isPotentialDirtyRead()).isTrue(); +// } +// cursor.close(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncArangos") +// void queryAllowRetry(ArangoDBAsync arangoDB) throws IOException { +// assumeTrue(isAtLeastVersion(3, 11)); +// final ArangoCursor cursor = arangoDB.db() +// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)); +// assertThat(cursor.asListRemaining()).containsExactly("1", "2"); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncArangos") +// void queryAllowRetryClose(ArangoDBAsync arangoDB) throws IOException { +// assumeTrue(isAtLeastVersion(3, 11)); +// final ArangoCursor cursor = arangoDB.db() +// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)); +// assertThat(cursor.hasNext()).isTrue(); +// assertThat(cursor.next()).isEqualTo("1"); +// assertThat(cursor.hasNext()).isTrue(); +// assertThat(cursor.next()).isEqualTo("2"); +// assertThat(cursor.hasNext()).isFalse(); +// cursor.close(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncArangos") +// void queryAllowRetryCloseBeforeLatestBatch(ArangoDBAsync arangoDB) throws IOException { +// assumeTrue(isAtLeastVersion(3, 11)); +// final ArangoCursor cursor = arangoDB.db() +// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)); +// assertThat(cursor.hasNext()).isTrue(); +// assertThat(cursor.next()).isEqualTo("1"); +// assertThat(cursor.hasNext()).isTrue(); +// cursor.close(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncArangos") +// void queryAllowRetryCloseSingleBatch(ArangoDBAsync arangoDB) throws IOException { +// assumeTrue(isAtLeastVersion(3, 11)); +// final ArangoCursor cursor = arangoDB.db() +// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true)); +// assertThat(cursor.hasNext()).isTrue(); +// assertThat(cursor.next()).isEqualTo("1"); +// assertThat(cursor.hasNext()).isTrue(); +// assertThat(cursor.next()).isEqualTo("2"); +// assertThat(cursor.hasNext()).isFalse(); +// cursor.close(); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void explainQuery(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final AqlExecutionExplainEntity explain = db.explainQuery("for i in 1..1 return i", null, null).get(); + assertThat(explain).isNotNull(); + assertThat(explain.getPlan()).isNotNull(); + assertThat(explain.getPlans()).isNull(); + final ExecutionPlan plan = explain.getPlan(); + assertThat(plan.getCollections()).isEmpty(); + assertThat(plan.getEstimatedCost()).isPositive(); + assertThat(plan.getEstimatedNrItems()).isPositive(); + assertThat(plan.getVariables()).hasSize(2); + assertThat(plan.getNodes()).isNotEmpty(); + if (isAtLeastVersion(3, 10)) { + assertThat(explain.getStats().getPeakMemoryUsage()).isNotNull(); + assertThat(explain.getStats().getExecutionTime()).isNotNull(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void explainQueryWithBindVars(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final AqlExecutionExplainEntity explain = db.explainQuery("for i in 1..1 return @value", + Collections.singletonMap("value", 11), null).get(); + assertThat(explain).isNotNull(); + assertThat(explain.getPlan()).isNotNull(); + assertThat(explain.getPlans()).isNull(); + final ExecutionPlan plan = explain.getPlan(); + assertThat(plan.getCollections()).isEmpty(); + assertThat(plan.getEstimatedCost()).isPositive(); + assertThat(plan.getEstimatedNrItems()).isPositive(); + assertThat(plan.getVariables()).hasSize(3); + assertThat(plan.getNodes()).isNotEmpty(); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void explainQueryWithIndexNode(ArangoDatabaseAsync db) { +// ArangoCollection character = db.collection("got_characters"); +// ArangoCollection actor = db.collection("got_actors"); +// +// if (!character.exists()) +// character.create(); +// +// if (!actor.exists()) +// actor.create(); +// +// String query = "" + +// "FOR `character` IN `got_characters` " + +// " FOR `actor` IN `got_actors` " + +// " FILTER `character`.`actor` == `actor`.`_id` " + +// " RETURN `character`"; +// +// final ExecutionPlan plan = db.explainQuery(query, null, null).getPlan(); +// plan.getNodes().stream() +// .filter(it -> "IndexNode".equals(it.getType())) +// .flatMap(it -> it.getIndexes().stream()) +// .forEach(it -> { +// assertThat(it.getType()).isEqualTo(IndexType.primary); +// assertThat(it.getFields()).contains("_key"); +// }); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void parseQuery(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final AqlParseEntity parse = db.parseQuery("for i in 1..1 return i").get(); + assertThat(parse).isNotNull(); + assertThat(parse.getBindVars()).isEmpty(); + assertThat(parse.getCollections()).isEmpty(); + assertThat(parse.getAst()).hasSize(1); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void getCurrentlyRunningQueries(ArangoDatabaseAsync db) throws InterruptedException { +// String query = "return sleep(1)"; +// Thread t = new Thread(() -> db.query(query, Void.class)); +// t.start(); +// Thread.sleep(300); +// final Collection currentlyRunningQueries = db.getCurrentlyRunningQueries(); +// assertThat(currentlyRunningQueries).hasSize(1); +// final QueryEntity queryEntity = currentlyRunningQueries.iterator().next(); +// assertThat(queryEntity.getId()).isNotNull(); +// assertThat(queryEntity.getDatabase()).isEqualTo(db.name()); +// assertThat(queryEntity.getUser()).isEqualTo("root"); +// assertThat(queryEntity.getQuery()).isEqualTo(query); +// assertThat(queryEntity.getBindVars()).isEmpty(); +// assertThat(queryEntity.getStarted()).isInThePast(); +// assertThat(queryEntity.getRunTime()).isPositive(); +// if (isAtLeastVersion(3, 11)) { +// assertThat(queryEntity.getPeakMemoryUsage()).isNotNull(); +// } +// assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.EXECUTING); +// assertThat(queryEntity.getStream()).isFalse(); +// t.join(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void killQuery(ArangoDatabaseAsync db) throws InterruptedException, ExecutionException { +// ExecutorService es = Executors.newSingleThreadExecutor(); +// Future future = es.submit(() -> { +// try { +// db.query("return sleep(5)", Void.class); +// fail(); +// } catch (ArangoDBException e) { +// assertThat(e.getResponseCode()).isEqualTo(410); +// assertThat(e.getErrorNum()).isEqualTo(1500); +// assertThat(e.getErrorMessage()).contains("query killed"); +// } +// }); +// Thread.sleep(500); +// +// Collection currentlyRunningQueries = db.getCurrentlyRunningQueries(); +// assertThat(currentlyRunningQueries).hasSize(1); +// QueryEntity queryEntity = currentlyRunningQueries.iterator().next(); +// assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.EXECUTING); +// db.killQuery(queryEntity.getId()); +// +// db.getCurrentlyRunningQueries().forEach(q -> +// assertThat(q.getState()).isEqualTo(QueryExecutionState.KILLED) +// ); +// +// future.get(); +// es.shutdown(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void getAndClearSlowQueries(ArangoDatabaseAsync db) { +// db.clearSlowQueries(); +// +// final QueryTrackingPropertiesEntity properties = db.getQueryTrackingProperties(); +// final Long slowQueryThreshold = properties.getSlowQueryThreshold(); +// properties.setSlowQueryThreshold(1L); +// db.setQueryTrackingProperties(properties); +// +// String query = "return sleep(1.1)"; +// db.query(query, Void.class); +// final Collection slowQueries = db.getSlowQueries(); +// assertThat(slowQueries).hasSize(1); +// final QueryEntity queryEntity = slowQueries.iterator().next(); +// assertThat(queryEntity.getId()).isNotNull(); +// assertThat(queryEntity.getDatabase()).isEqualTo(db.name()); +// assertThat(queryEntity.getUser()).isEqualTo("root"); +// assertThat(queryEntity.getQuery()).isEqualTo(query); +// assertThat(queryEntity.getBindVars()).isEmpty(); +// assertThat(queryEntity.getStarted()).isInThePast(); +// assertThat(queryEntity.getRunTime()).isPositive(); +// if (isAtLeastVersion(3, 11)) { +// assertThat(queryEntity.getPeakMemoryUsage()).isNotNull(); +// } +// assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.FINISHED); +// assertThat(queryEntity.getStream()).isFalse(); +// +// db.clearSlowQueries(); +// assertThat(db.getSlowQueries()).isEmpty(); +// properties.setSlowQueryThreshold(slowQueryThreshold); +// db.setQueryTrackingProperties(properties); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createGetDeleteAqlFunction(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Collection aqlFunctionsInitial = db.getAqlFunctions(null).get(); + assertThat(aqlFunctionsInitial).isEmpty(); + try { + db.createAqlFunction("myfunctions::temperature::celsiustofahrenheit", + "function (celsius) { return celsius * 1.8 + 32; }", null).get(); + + final Collection aqlFunctions = db.getAqlFunctions(null).get(); + assertThat(aqlFunctions).hasSizeGreaterThan(aqlFunctionsInitial.size()); + } finally { + final Integer deleteCount = db.deleteAqlFunction("myfunctions::temperature::celsiustofahrenheit", null).get(); + // compatibility with ArangoDB < 3.4 + if (isAtLeastVersion(3, 4)) { + assertThat(deleteCount).isEqualTo(1); + } else { + assertThat(deleteCount).isNull(); + } + final Collection aqlFunctions = db.getAqlFunctions(null).get(); + assertThat(aqlFunctions).hasSize(aqlFunctionsInitial.size()); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createGetDeleteAqlFunctionWithNamespace(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Collection aqlFunctionsInitial = db.getAqlFunctions(null).get(); + assertThat(aqlFunctionsInitial).isEmpty(); + try { + db.createAqlFunction("myfunctions::temperature::celsiustofahrenheit1", + "function (celsius) { return celsius * 1.8 + 32; }", null).get(); + db.createAqlFunction("myfunctions::temperature::celsiustofahrenheit2", + "function (celsius) { return celsius * 1.8 + 32; }", null).get(); + + } finally { + final Integer deleteCount = db + .deleteAqlFunction("myfunctions::temperature", new AqlFunctionDeleteOptions().group(true)).get(); + // compatibility with ArangoDB < 3.4 + if (isAtLeastVersion(3, 4)) { + assertThat(deleteCount).isEqualTo(2); + } else { + assertThat(deleteCount).isNull(); + } + final Collection aqlFunctions = db.getAqlFunctions(null).get(); + assertThat(aqlFunctions).hasSize(aqlFunctionsInitial.size()); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createGraph(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + String name = "graph-" + rnd(); + final GraphEntity result = db.createGraph(name, null, null).get(); + assertThat(result.getName()).isEqualTo(name); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createGraphSatellite(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + assumeTrue(isCluster()); + assumeTrue(isEnterprise()); + + String name = "graph-" + rnd(); + final GraphEntity result = db.createGraph(name, null, new GraphCreateOptions().replicationFactor(ReplicationFactor.ofSatellite())).get(); + assertThat(result.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + +// GraphEntity info = db.graph(name).getInfo(); +// assertThat(info.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); +// +// GraphEntity graph = db.getGraphs().stream().filter(g -> name.equals(g.getName())).findFirst().get(); +// assertThat(graph.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createGraphReplicationFaktor(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + String name = "graph-" + rnd(); + final String edgeCollection = rndName(); + final String fromCollection = rndName(); + final String toCollection = rndName(); + final Collection edgeDefinitions = + Collections.singletonList(new EdgeDefinition().collection(edgeCollection).from(fromCollection).to(toCollection)); + final GraphEntity result = db.createGraph(name, edgeDefinitions, new GraphCreateOptions().replicationFactor(2)).get(); + assertThat(result).isNotNull(); +// for (final String collection : Arrays.asList(edgeCollection, fromCollection, toCollection)) { +// final CollectionPropertiesEntity properties = db.collection(collection).getProperties(); +// assertThat(properties.getReplicationFactor().get()).isEqualTo(2); +// } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createGraphNumberOfShards(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + String name = "graph-" + rnd(); + final String edgeCollection = rndName(); + final String fromCollection = rndName(); + final String toCollection = rndName(); + final Collection edgeDefinitions = + Collections.singletonList(new EdgeDefinition().collection(edgeCollection).from(fromCollection).to(toCollection)); + final GraphEntity result = db + .createGraph(name, edgeDefinitions, new GraphCreateOptions().numberOfShards(2)).get(); + assertThat(result).isNotNull(); +// for (final String collection : Arrays.asList(edgeCollection, fromCollection, toCollection)) { +// final CollectionPropertiesEntity properties = db.collection(collection).getProperties(); +// assertThat(properties.getNumberOfShards()).isEqualTo(2); +// } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getGraphs(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + String name = "graph-" + rnd(); + db.createGraph(name, null, null).get(); + final Collection graphs = db.getGraphs().get(); + assertThat(graphs).hasSizeGreaterThanOrEqualTo(1); + long count = graphs.stream().map(GraphEntity::getName).filter(name::equals).count(); + assertThat(count).isEqualTo(1L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionString(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final TransactionOptions options = new TransactionOptions().params("test"); + final RawJson result = db.transaction("function (params) {return params;}", RawJson.class, options).get(); + assertThat(result.get()).isEqualTo("\"test\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionNumber(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final TransactionOptions options = new TransactionOptions().params(5); + final Integer result = db.transaction("function (params) {return params;}", Integer.class, options).get(); + assertThat(result).isEqualTo(5); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionJsonNode(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final TransactionOptions options = new TransactionOptions().params(JsonNodeFactory.instance.textNode("test")); + final JsonNode result = db.transaction("function (params) {return params;}", JsonNode.class, options).get(); + assertThat(result.isTextual()).isTrue(); + assertThat(result.asText()).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionJsonObject(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + ObjectNode params = JsonNodeFactory.instance.objectNode().put("foo", "hello").put("bar", "world"); + final TransactionOptions options = new TransactionOptions().params(params); + final RawJson result = db + .transaction("function (params) { return params['foo'] + ' ' + params['bar'];}", RawJson.class, + options).get(); + assertThat(result.get()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionJsonArray(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + ArrayNode params = JsonNodeFactory.instance.arrayNode().add("hello").add("world"); + final TransactionOptions options = new TransactionOptions().params(params); + final RawJson result = db + .transaction("function (params) { return params[0] + ' ' + params[1];}", RawJson.class, options).get(); + assertThat(result.get()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionMap(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Map params = new MapBuilder().put("foo", "hello").put("bar", "world").get(); + final TransactionOptions options = new TransactionOptions().params(params); + final RawJson result = db + .transaction("function (params) { return params['foo'] + ' ' + params['bar'];}", RawJson.class, + options).get(); + assertThat(result.get()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionArray(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final String[] params = new String[]{"hello", "world"}; + final TransactionOptions options = new TransactionOptions().params(params); + final RawJson result = db + .transaction("function (params) { return params[0] + ' ' + params[1];}", RawJson.class, options).get(); + assertThat(result.get()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Collection params = new ArrayList<>(); + params.add("hello"); + params.add("world"); + final TransactionOptions options = new TransactionOptions().params(params); + final RawJson result = db + .transaction("function (params) { return params[0] + ' ' + params[1];}", RawJson.class, options).get(); + assertThat(result.get()).isEqualTo("\"hello world\""); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void transactionInsertJson(ArangoDatabaseAsync db) { +// String key = "key-" + rnd(); +// final TransactionOptions options = new TransactionOptions().params("{\"_key\":\"" + key + "\"}") +// .writeCollections(CNAME1); +// db.transaction("function (params) { " +// + "var db = require('internal').db;" +// + "db." + CNAME1 + ".save(JSON.parse(params));" +// + "}", Void.class, options); +// assertThat(db.collection(CNAME1).getDocument(key, RawJson.class)).isNotNull(); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void transactionExclusiveWrite(ArangoDatabaseAsync db) { +// assumeTrue(isAtLeastVersion(3, 4)); +// String key = "key-" + rnd(); +// final TransactionOptions options = new TransactionOptions().params("{\"_key\":\"" + key + "\"}") +// .exclusiveCollections(CNAME1); +// db.transaction("function (params) { " +// + "var db = require('internal').db;" +// + "db." + CNAME1 + ".save(JSON.parse(params));" +// + "}", Void.class, options); +// assertThat(db.collection(CNAME1).getDocument(key, RawJson.class)).isNotNull(); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionEmpty(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + db.transaction("function () {}", Void.class, null).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionAllowImplicit(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final String action = "function (params) {" + "var db = require('internal').db;" + + "return {'a':db." + CNAME1 + ".all().toArray()[0], 'b':db." + CNAME2 + ".all().toArray()[0]};" + + "}"; + final TransactionOptions options = new TransactionOptions().readCollections(CNAME1); + db.transaction(action, JsonNode.class, options).get(); + options.allowImplicit(false); + Throwable thrown = catchThrowable(() -> db.transaction(action, JsonNode.class, options).get()).getCause(); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .extracting(it -> ((ArangoDBException) it).getResponseCode()) + .isEqualTo(400); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionPojoReturn(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final String action = "function() { return {'value':'hello world'}; }"; + final TransactionTestEntity res = db.transaction(action, TransactionTestEntity.class, new TransactionOptions()).get(); + assertThat(res).isNotNull(); + assertThat(res.value).isEqualTo("hello world"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getInfo(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final DatabaseEntity info = db.getInfo().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(TEST_DB); + assertThat(info.getPath()).isNotNull(); + assertThat(info.getIsSystem()).isFalse(); + + if (isAtLeastVersion(3, 6) && isCluster()) { + assertThat(info.getSharding()).isNotNull(); + assertThat(info.getWriteConcern()).isNotNull(); + assertThat(info.getReplicationFactor()).isNotNull(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void shouldIncludeExceptionMessage(ArangoDatabaseAsync db) { + assumeTrue(isAtLeastVersion(3, 4)); + + final String exceptionMessage = "My error context"; + final String action = "function (params) {" + "throw '" + exceptionMessage + "';" + "}"; + Throwable thrown = catchThrowable(() -> db.transaction(action, Void.class, null).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) thrown).getErrorMessage()).isEqualTo(exceptionMessage); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void reloadRouting(ArangoDatabaseAsync db) { + db.reloadRouting(); + } + + public static class TransactionTestEntity { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } +} diff --git a/driver/src/test/java/com/arangodb/BaseJunit5.java b/driver/src/test/java/com/arangodb/BaseJunit5.java index e7a4f7740..a907cf2d1 100644 --- a/driver/src/test/java/com/arangodb/BaseJunit5.java +++ b/driver/src/test/java/com/arangodb/BaseJunit5.java @@ -30,22 +30,35 @@ class BaseJunit5 { private static Boolean extendedDbNames; private static Boolean extendedNames; + protected static Stream adbsStream() { + return adbs.stream(); + } protected static Stream dbsStream() { - return adbs.stream().map(adb -> adb.db(TEST_DB)); + return adbsStream().map(adb -> adb.db(TEST_DB)); + } + protected static Stream asyncAdbsStream() { + return adbs.stream().map(ArangoDB::async); + } + protected static Stream asyncDbsStream() { + return asyncAdbsStream().map(adb -> adb.db(TEST_DB)); } protected static Stream arangos() { - return adbs.stream().map(Arguments::of); + return adbsStream().map(Arguments::of); } protected static Stream asyncArangos() { - return adbs.stream().map(ArangoDB::async).map(Arguments::of); + return asyncAdbsStream().map(Arguments::of); } protected static Stream dbs() { return dbsStream().map(Arguments::of); } + protected static Stream asyncDbs() { + return asyncDbsStream().map(Arguments::of); + } + static ArangoDatabase initDB(String name) { ArangoDatabase database = adbs.get(0).db(name); if (!database.exists()) From a2654c43520b4d65f15414c9a83c6a5bdc5de11b Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 12 Oct 2023 17:45:23 +0200 Subject: [PATCH 19/62] ArangoCollectionAsync --- .../com/arangodb/ArangoCollectionAsync.java | 641 +++--------------- 1 file changed, 77 insertions(+), 564 deletions(-) diff --git a/core/src/main/java/com/arangodb/ArangoCollectionAsync.java b/core/src/main/java/com/arangodb/ArangoCollectionAsync.java index 7549f531f..f3dd03484 100644 --- a/core/src/main/java/com/arangodb/ArangoCollectionAsync.java +++ b/core/src/main/java/com/arangodb/ArangoCollectionAsync.java @@ -29,879 +29,392 @@ import java.util.concurrent.CompletableFuture; /** - * Interface for operations on ArangoDB collection level. - * - * @author Mark Vollmary - * @author Heiko Kernbach - * @author Michele Rastelli - * @see Documents API Documentation + * Asynchronous version of {@link ArangoCollection} */ @ThreadSafe public interface ArangoCollectionAsync extends ArangoSerdeAccessor { -// FIXME: -// /** -// * The the handler of the database the collection is within -// * -// * @return database handler -// */ -// ArangoDatabaseAsync db(); + /** + * @return database async API + */ + ArangoDatabaseAsync db(); /** - * The name of the collection - * * @return collection name */ String name(); /** - * Creates a new document from the given document, unless there is already a document with the _key given. If no - * _key is given, a new unique _key is generated automatically. - * - * @param value A representation of a single document (POJO or {@link RawData} - * @return information about the document - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#insertDocument(Object)} */ CompletableFuture> insertDocument(Object value); /** - * Creates a new document from the given document, unless there is already a document with the _key given. If no - * _key is given, a new unique _key is generated automatically. - * - * @param value A representation of a single document (POJO or {@link RawData}) - * @param options Additional options - * @return information about the document - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#insertDocument(Object, DocumentCreateOptions)} */ CompletableFuture> insertDocument(T value, DocumentCreateOptions options); /** - * Creates a new document from the given document, unless there is already a document with the _key given. If no - * _key is given, a new unique _key is generated automatically. - * - * @param value A representation of a single document (POJO or {@link RawData}) - * @param options Additional options - * @param type Deserialization target type for the returned documents. - * @return information about the document - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#insertDocument(Object, DocumentCreateOptions, Class)} */ CompletableFuture> insertDocument(T value, DocumentCreateOptions options, Class type); /** - * Creates new documents from the given documents, unless there is already a document with the _key given. If no - * _key is given, a new unique _key is generated automatically. - * - * @param values Raw data representing a collection of documents - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#insertDocuments(RawData)} */ CompletableFuture>> insertDocuments(RawData values); /** - * Creates new documents from the given documents, unless there is already a document with the _key given. If no - * _key is given, a new unique _key is generated automatically. - * - * @param values Raw data representing a collection of documents - * @param options Additional options - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#insertDocuments(RawData, DocumentCreateOptions)} */ CompletableFuture>> insertDocuments( RawData values, DocumentCreateOptions options); /** - * Creates new documents from the given documents, unless there is already a document with the _key given. If no - * _key is given, a new unique _key is generated automatically. - * - * @param values A List of documents - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#insertDocuments(Iterable)} */ CompletableFuture>> insertDocuments(Iterable values); /** - * Creates new documents from the given documents, unless there is already a document with the _key given. If no - * _key is given, a new unique _key is generated automatically. - * - * @param values A List of documents (POJO or {@link RawData}) - * @param options Additional options - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#insertDocuments(Iterable, DocumentCreateOptions)} */ CompletableFuture>> insertDocuments( Iterable values, DocumentCreateOptions options); /** - * Creates new documents from the given documents, unless there is already a document with the _key given. If no - * _key is given, a new unique _key is generated automatically. - * - * @param values A List of documents (POJO or {@link RawData}) - * @param options Additional options - * @param type Deserialization target type for the returned documents. - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#insertDocuments(Iterable, DocumentCreateOptions, Class)} */ CompletableFuture>> insertDocuments( Iterable values, DocumentCreateOptions options, Class type); /** - * Bulk imports the given values into the collection. - * - * @param values A List of documents (POJO or {@link RawData}) - * @return information about the import + * Asynchronous version of {@link ArangoCollection#importDocuments(Iterable)} */ CompletableFuture importDocuments(Iterable values); /** - * Bulk imports the given values into the collection. - * - * @param values A List of documents (POJO or {@link RawData}) - * @param options Additional options, can be null - * @return information about the import + * Asynchronous version of {@link ArangoCollection#importDocuments(Iterable, DocumentImportOptions)} */ CompletableFuture importDocuments(Iterable values, DocumentImportOptions options); /** - * Bulk imports the given values into the collection. - * - * @param values Raw data representing a collection of documents - * @return information about the import + * Asynchronous version of {@link ArangoCollection#importDocuments(RawData)} */ CompletableFuture importDocuments(RawData values); /** - * Bulk imports the given values into the collection. - * - * @param values Raw data representing a collection of documents - * @param options Additional options, can be null - * @return information about the import + * Asynchronous version of {@link ArangoCollection#importDocuments(RawData, DocumentImportOptions)} */ CompletableFuture importDocuments(RawData values, DocumentImportOptions options); /** - * Retrieves the document with the given {@code key} from the collection. - * - * @param key The key of the document - * @param type The type of the document (POJO or {@link RawData}) - * @return the document identified by the key - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#getDocument(String, Class)} */ CompletableFuture getDocument(String key, Class type); /** - * Retrieves the document with the given {@code key} from the collection. - * - * @param key The key of the document - * @param type The type of the document (POJO or {@link RawData}) - * @param options Additional options, can be null - * @return the document identified by the key - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#getDocument(String, Class, DocumentReadOptions)} */ CompletableFuture getDocument(String key, Class type, DocumentReadOptions options); /** - * Retrieves multiple documents with the given {@code _key} from the collection. - * - * @param keys The keys of the documents - * @param type The type of the documents (POJO or {@link RawData}) - * @return the documents and possible errors + * Asynchronous version of {@link ArangoCollection#getDocuments(Iterable, Class)} */ CompletableFuture> getDocuments(Iterable keys, Class type); /** - * Retrieves multiple documents with the given {@code _key} from the collection. - * - * @param keys The keys of the documents - * @param type The type of the documents (POJO or {@link RawData}) - * @param options Additional options, can be null - * @return the documents and possible errors + * Asynchronous version of {@link ArangoCollection#getDocuments(Iterable, Class, DocumentReadOptions)} */ CompletableFuture> getDocuments(Iterable keys, Class type, DocumentReadOptions options); /** - * Replaces the document with {@code key} with the one in the body, provided there is such a document and no - * precondition is violated - * - * @param key The key of the document - * @param value A representation of a single document (POJO or {@link RawData}) - * @return information about the document - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#replaceDocument(String, Object)} */ CompletableFuture> replaceDocument(String key, Object value); /** - * Replaces the document with {@code key} with the one in the body, provided there is such a document and no - * precondition is violated - * - * @param key The key of the document - * @param value A representation of a single document (POJO or {@link RawData}) - * @param options Additional options - * @return information about the document - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#replaceDocument(String, Object, DocumentReplaceOptions)} */ CompletableFuture> replaceDocument(String key, T value, DocumentReplaceOptions options); /** - * Replaces the document with {@code key} with the one in the body, provided there is such a document and no - * precondition is violated - * - * @param key The key of the document - * @param value A representation of a single document (POJO or {@link RawData}) - * @param options Additional options - * @param type Deserialization target type for the returned documents. - * @return information about the document - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#replaceDocument(String, Object, DocumentReplaceOptions, Class)} */ CompletableFuture> replaceDocument(String key, T value, DocumentReplaceOptions options, Class type); /** - * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are - * specified by the _key attributes in the documents in values. - * - * @param values Raw data representing a collection of documents - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#replaceDocuments(RawData)} */ CompletableFuture>> replaceDocuments(RawData values); /** - * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are - * specified by the _key attributes in the documents in values. - * - * @param values Raw data representing a collection of documents - * @param options Additional options - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#replaceDocuments(RawData, DocumentReplaceOptions)} */ CompletableFuture>> replaceDocuments( RawData values, DocumentReplaceOptions options); /** - * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are - * specified by the _key attributes in the documents in values. - * - * @param values A List of documents (POJO or {@link RawData}) - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#replaceDocuments(Iterable)} )} */ CompletableFuture>> replaceDocuments(Iterable values); /** - * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are - * specified by the _key attributes in the documents in values. - * - * @param values A List of documents (POJO or {@link RawData}) - * @param options Additional options - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#replaceDocuments(Iterable, DocumentReplaceOptions)} */ CompletableFuture>> replaceDocuments( Iterable values, DocumentReplaceOptions options); /** - * Replaces multiple documents in the specified collection with the ones in the values, the replaced documents are - * specified by the _key attributes in the documents in values. - * - * @param values A List of documents (POJO or {@link RawData}) - * @param options Additional options - * @param type Deserialization target type for the returned documents. - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#replaceDocuments(Iterable, DocumentReplaceOptions, Class)} */ CompletableFuture>> replaceDocuments( Iterable values, DocumentReplaceOptions options, Class type); /** - * Partially updates the document identified by document-key. The value must contain a document with the attributes - * to patch (the patch document). All attributes from the patch document will be added to the existing document if - * they do not yet exist, and overwritten in the existing document if they do exist there. - * - * @param key The key of the document - * @param value A representation of a single document (POJO or {@link RawData}) - * @return information about the document - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#updateDocument(String, Object)} */ CompletableFuture> updateDocument(String key, Object value); /** - * Partially updates the document identified by document-key. The value must contain a document with the attributes - * to patch (the patch document). All attributes from the patch document will be added to the existing document if - * they do not yet exist, and overwritten in the existing document if they do exist there. - * - * @param key The key of the document - * @param value A representation of a single document (POJO or {@link RawData}) - * @param options Additional options - * @return information about the document - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#updateDocument(String, Object, DocumentUpdateOptions)} */ CompletableFuture> updateDocument(String key, T value, DocumentUpdateOptions options); /** - * Partially updates the document identified by document-key. The value must contain a document with the attributes - * to patch (the patch document). All attributes from the patch document will be added to the existing document if - * they do not yet exist, and overwritten in the existing document if they do exist there. - * - * @param key The key of the document - * @param value A representation of a single document (POJO or {@link RawData}) - * @param options Additional options - * @param returnType Type of the returned newDocument and/or oldDocument - * @return information about the document - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#updateDocument(String, Object, DocumentUpdateOptions, Class)} */ CompletableFuture> updateDocument(String key, Object value, DocumentUpdateOptions options, Class returnType); /** - * Partially updates documents, the documents to update are specified by the _key attributes in the objects on - * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All - * attributes from the patch documents will be added to the existing documents if they do not yet exist, and - * overwritten in the existing documents if they do exist there. - * - * @param values Raw data representing a collection of documents - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#updateDocuments(RawData)} */ CompletableFuture>> updateDocuments(RawData values); /** - * Partially updates documents, the documents to update are specified by the _key attributes in the objects on - * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All - * attributes from the patch documents will be added to the existing documents if they do not yet exist, and - * overwritten in the existing documents if they do exist there. - * - * @param values Raw data representing a collection of documents - * @param options Additional options - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#updateDocuments(RawData, DocumentUpdateOptions)} */ CompletableFuture>> updateDocuments( RawData values, DocumentUpdateOptions options); /** - * Partially updates documents, the documents to update are specified by the _key attributes in the objects on - * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All - * attributes from the patch documents will be added to the existing documents if they do not yet exist, and - * overwritten in the existing documents if they do exist there. - * - * @param values A list of documents (POJO or {@link RawData}) - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#updateDocuments(Iterable)} */ CompletableFuture>> updateDocuments(Iterable values); /** - * Partially updates documents, the documents to update are specified by the _key attributes in the objects on - * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All - * attributes from the patch documents will be added to the existing documents if they do not yet exist, and - * overwritten in the existing documents if they do exist there. - * - * @param values A list of documents (POJO or {@link RawData}) - * @param options Additional options - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#updateDocuments(Iterable, DocumentUpdateOptions)} */ CompletableFuture>> updateDocuments( Iterable values, DocumentUpdateOptions options); /** - * Partially updates documents, the documents to update are specified by the _key attributes in the objects on - * values. Vales must contain a list of document updates with the attributes to patch (the patch documents). All - * attributes from the patch documents will be added to the existing documents if they do not yet exist, and - * overwritten in the existing documents if they do exist there. - * - * @param values A list of documents (POJO or {@link RawData}) - * @param options Additional options - * @param returnType Type of the returned newDocument and/or oldDocument - * @return information about the documents - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#updateDocuments(Iterable, DocumentUpdateOptions, Class)} */ CompletableFuture>> updateDocuments( Iterable values, DocumentUpdateOptions options, Class returnType); /** - * Deletes the document with the given {@code key} from the collection. - * - * @param key The key of the document - * @return information about the document - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#deleteDocument(String)} */ CompletableFuture> deleteDocument(String key); /** - * Deletes the document with the given {@code key} from the collection. - * - * @param key The key of the document - * @param options Additional options - * @return information about the document - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#deleteDocument(String, DocumentDeleteOptions)} */ CompletableFuture> deleteDocument(String key, DocumentDeleteOptions options); /** - * Deletes the document with the given {@code key} from the collection. - * - * @param key The key of the document - * @param type Deserialization target type for the returned documents. - * @param options Additional options - * @return information about the document - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#deleteDocument(String, DocumentDeleteOptions, Class)} */ CompletableFuture> deleteDocument(String key, DocumentDeleteOptions options, Class type); /** - * Deletes multiple documents from the collection. - * - * @param values Raw data representing the keys of the documents or the documents themselves - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#deleteDocuments(RawData)} */ CompletableFuture>> deleteDocuments(RawData values); /** - * Deletes multiple documents from the collection. - * - * @param values Raw data representing the keys of the documents or the documents themselves - * @param options Additional options - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#deleteDocuments(RawData, DocumentDeleteOptions)} */ CompletableFuture>> deleteDocuments( RawData values, DocumentDeleteOptions options); /** - * Deletes multiple documents from the collection. - * - * @param values The keys of the documents or the documents themselves - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#deleteDocuments(Iterable)} */ CompletableFuture>> deleteDocuments(Iterable values); /** - * Deletes multiple documents from the collection. - * - * @param values The keys of the documents or the documents themselves - * @param options Additional options - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#deleteDocuments(Iterable, DocumentDeleteOptions)} */ CompletableFuture>> deleteDocuments( Iterable values, DocumentDeleteOptions options); /** - * Deletes multiple documents from the collection. - * - * @param values The keys of the documents or the documents themselves - * @param type Deserialization target type for the returned documents. - * @param options Additional options - * @return information about the documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#deleteDocuments(Iterable, DocumentDeleteOptions, Class)} */ CompletableFuture>> deleteDocuments( Iterable values, DocumentDeleteOptions options, Class type); /** - * Checks if the document exists by reading a single document head - * - * @param key The key of the document - * @return true if the document was found, otherwise false - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#documentExists(String)} */ CompletableFuture documentExists(String key); /** - * Checks if the document exists by reading a single document head - * - * @param key The key of the document - * @param options Additional options, can be null - * @return true if the document was found, otherwise false - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#documentExists(String, DocumentExistsOptions)} */ CompletableFuture documentExists(String key, DocumentExistsOptions options); /** - * Fetches information about the index with the given {@code id} and returns it. - *
- * Note: inverted indexes are not returned by this method. Use - * {@link ArangoCollectionAsync#getInvertedIndex(String)} instead. - * - * @param id The index-handle - * @return information about the index - * @see - * API Documentation + * Asynchronous version of {@link ArangoCollection#getIndex(String)} */ CompletableFuture getIndex(String id); /** - * Fetches information about the inverted index with the given {@code id} and returns it. - * - * @param id The index-handle - * @return information about the index - * @see API Documentation - * @since ArangoDB 3.10 + * Asynchronous version of {@link ArangoCollection#getInvertedIndex(String)} */ CompletableFuture getInvertedIndex(String id); /** - * Deletes the index with the given {@code id} from the collection. - * - * @param id The index-handle - * @return the id of the index - * @see - * API Documentation + * Asynchronous version of {@link ArangoCollection#deleteIndex(String)} */ CompletableFuture deleteIndex(String id); /** - * Creates a persistent index for the collection, if it does not already exist. - * - * @param fields A list of attribute paths - * @param options Additional options, can be null - * @return information about the index - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#ensurePersistentIndex(Iterable, PersistentIndexOptions)} */ CompletableFuture ensurePersistentIndex(Iterable fields, PersistentIndexOptions options); /** - * Creates a geo-spatial index for the collection, if it does not already exist. - * - * @param fields A list of attribute paths - * @param options Additional options, can be null - * @return information about the index - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#ensureGeoIndex(Iterable, GeoIndexOptions)} */ CompletableFuture ensureGeoIndex(Iterable fields, GeoIndexOptions options); /** - * Creates a fulltext index for the collection, if it does not already exist. - * - * @param fields A list of attribute paths - * @param options Additional options, can be null - * @return information about the index - * @see API - * Documentation - * @deprecated since ArangoDB 3.10, use ArangoSearch or Inverted indexes instead. + * Asynchronous version of {@link ArangoCollection#ensureFulltextIndex(Iterable, FulltextIndexOptions)} */ @Deprecated CompletableFuture ensureFulltextIndex(Iterable fields, FulltextIndexOptions options); /** - * Creates a ttl index for the collection, if it does not already exist. - * - * @param fields A list of attribute paths - * @param options Additional options, can be null - * @return information about the index - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#ensureTtlIndex(Iterable, TtlIndexOptions)} */ CompletableFuture ensureTtlIndex(Iterable fields, TtlIndexOptions options); /** - * Creates a ZKD multi-dimensional index for the collection, if it does not already exist. - * Note that zkd indexes are an experimental feature in ArangoDB 3.9. - * - * @param fields A list of attribute paths - * @param options Additional options, can be null - * @return information about the index - * @see API Documentation - * @since ArangoDB 3.9 + * Asynchronous version of {@link ArangoCollection#ensureZKDIndex(Iterable, ZKDIndexOptions)} */ CompletableFuture ensureZKDIndex(Iterable fields, ZKDIndexOptions options); /** - * Creates an inverted index for the collection, if it does not already exist. - * - * @param options index creation options - * @return information about the index - * @see API Documentation - * @since ArangoDB 3.10 + * Asynchronous version of {@link ArangoCollection#ensureInvertedIndex(InvertedIndexOptions)} */ CompletableFuture ensureInvertedIndex(InvertedIndexOptions options); /** - * Fetches a list of all indexes on this collection. - *
- * Note: inverted indexes are not returned by this method. Use - * {@link ArangoCollectionAsync#getInvertedIndexes()} instead. - * - * @return information about the indexes - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#getIndexes()} */ CompletableFuture> getIndexes(); /** - * Fetches a list of all inverted indexes on this collection. - * - * @return information about the indexes - * @see API - * Documentation - * @since ArangoDB 3.10 + * Asynchronous version of {@link ArangoCollection#getInvertedIndexes()} */ CompletableFuture> getInvertedIndexes(); /** - * Checks whether the collection exists - * - * @return true if the collection exists, otherwise false + * Asynchronous version of {@link ArangoCollection#exists()} */ CompletableFuture exists(); /** - * Removes all documents from the collection, but leaves the indexes intact - * - * @return information about the collection - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#truncate()} */ CompletableFuture truncate(); /** - * Removes all documents from the collection, but leaves the indexes intact - * - * @param options - * @return information about the collection - * @see API - * Documentation - * @since ArangoDB 3.5.0 + * Asynchronous version of {@link ArangoCollection#truncate(CollectionTruncateOptions)} */ CompletableFuture truncate(CollectionTruncateOptions options); /** - * Counts the documents in a collection - * - * @return information about the collection, including the number of documents - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#count()} */ CompletableFuture count(); /** - * Counts the documents in a collection - * - * @param options - * @return information about the collection, including the number of documents - * @see API - * Documentation - * @since ArangoDB 3.5.0 + * Asynchronous version of {@link ArangoCollection#count(CollectionCountOptions)} */ CompletableFuture count(CollectionCountOptions options); /** - * Creates a collection for this collection's name, then returns collection information from the server. - * - * @return information about the collection - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#create()} */ CompletableFuture create(); /** - * Creates a collection with the given {@code options} for this collection's name, then returns collection - * information from the server. - * - * @param options Additional options, can be null - * @return information about the collection - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#create(CollectionCreateOptions)} */ CompletableFuture create(CollectionCreateOptions options); /** - * Deletes the collection from the database. - * - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#drop()} */ CompletableFuture drop(); /** - * Deletes the collection from the database. - * - * @param isSystem Whether or not the collection to drop is a system collection. This parameter must be set to - * true in - * order to drop a system collection. - * @see API - * Documentation - * @since ArangoDB 3.1.0 + * Asynchronous version of {@link ArangoCollection#drop(boolean)} */ CompletableFuture drop(boolean isSystem); /** - * Returns information about the collection - * - * @return information about the collection - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#getInfo()} */ CompletableFuture getInfo(); /** - * Reads the properties of the specified collection - * - * @return properties of the collection - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#getProperties()} */ CompletableFuture getProperties(); /** - * Changes the properties of the collection - * - * @param options Additional options, can be null - * @return properties of the collection - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#changeProperties(CollectionPropertiesOptions)} */ CompletableFuture changeProperties(CollectionPropertiesOptions options); /** - * Renames the collection - * - * @param newName The new name - * @return information about the collection - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#rename(String)} */ CompletableFuture rename(String newName); /** - * Returns the responsible shard for the document. - * Please note that this API is only meaningful and available on a cluster coordinator. - * - * @param value A projection of the document containing at least the shard key (_key or a custom attribute) for - * which the responsible shard should be determined - * @return information about the responsible shard - * @see - * - * API Documentation - * @since ArangoDB 3.5.0 + * Asynchronous version of {@link ArangoCollection#getResponsibleShard(Object)} */ CompletableFuture getResponsibleShard(final Object value); /** - * Retrieve the collections revision - * - * @return information about the collection, including the collections revision - * @see - * API - * Documentation + * Asynchronous version of {@link ArangoCollection#getRevision()} */ CompletableFuture getRevision(); /** - * Grants or revoke access to the collection for user user. You need permission to the _system database in order to - * execute this call. - * - * @param user The name of the user - * @param permissions The permissions the user grant - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#grantAccess(String, Permissions)} */ CompletableFuture grantAccess(String user, Permissions permissions); /** - * Revokes access to the collection for user user. You need permission to the _system database in order to execute - * this call. - * - * @param user The name of the user - * @see API - * Documentation + * Asynchronous version of {@link ArangoCollection#revokeAccess(String)} */ CompletableFuture revokeAccess(String user); /** - * Clear the collection access level, revert back to the default access level. - * - * @param user The name of the user - * @see API - * Documentation - * @since ArangoDB 3.2.0 + * Asynchronous version of {@link ArangoCollection#resetAccess(String)} */ CompletableFuture resetAccess(String user); /** - * Get the collection access level - * - * @param user The name of the user - * @return permissions of the user - * @see - * - * API Documentation - * @since ArangoDB 3.2.0 + * Asynchronous version of {@link ArangoCollection#getPermissions(String)} */ CompletableFuture getPermissions(String user); From 20bd15afbc0fb3431c5d5869cd9a8ead4d8c2526 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 12 Oct 2023 21:21:19 +0200 Subject: [PATCH 20/62] ArangoCollectionAsyncImpl --- .../com/arangodb/ArangoDatabaseAsync.java | 32 +- .../internal/ArangoCollectionAsyncImpl.java | 514 ++++++++++++++++++ .../internal/ArangoCollectionImpl.java | 11 +- .../internal/ArangoDatabaseAsyncImpl.java | 34 +- .../internal/InternalArangoCollection.java | 71 ++- .../internal/net/CommunicationProtocol.java | 8 +- .../java/com/arangodb/ArangoDBAsyncTest.java | 23 +- .../com/arangodb/ArangoDatabaseAsyncTest.java | 304 +++++------ 8 files changed, 755 insertions(+), 242 deletions(-) create mode 100644 core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java diff --git a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java index d0ec27bf6..b4a10262b 100644 --- a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java @@ -68,13 +68,13 @@ public interface ArangoDatabaseAsync extends ArangoSerdeAccessor { */ CompletableFuture> getAccessibleDatabases(); -// /** -// * Returns a {@code ArangoCollection} instance for the given collection name. -// * -// * @param name Name of the collection -// * @return collection handler -// */ -// ArangoCollection collection(String name); + /** + * Returns a {@code ArangoCollectionAsync} instance for the given collection name. + * + * @param name Name of the collection + * @return collection handler + */ + ArangoCollectionAsync collection(String name); /** * Asynchronous version of {@link ArangoDatabase#createCollection(String)} @@ -96,15 +96,15 @@ public interface ArangoDatabaseAsync extends ArangoSerdeAccessor { */ CompletableFuture> getCollections(CollectionsReadOptions options); -// /** -// * Asynchronous version of {@link ArangoDatabase#getIndex(String)} -// */ -// CompletableFuture getIndex(String id); -// -// /** -// * Asynchronous version of {@link ArangoDatabase#deleteIndex(String)} -// */ -// CompletableFuture deleteIndex(String id); + /** + * Asynchronous version of {@link ArangoDatabase#getIndex(String)} + */ + CompletableFuture getIndex(String id); + + /** + * Asynchronous version of {@link ArangoDatabase#deleteIndex(String)} + */ + CompletableFuture deleteIndex(String id); /** * Asynchronous version of {@link ArangoDatabase#create()} diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java new file mode 100644 index 000000000..5caa4acd2 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java @@ -0,0 +1,514 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.*; +import com.arangodb.entity.*; +import com.arangodb.internal.util.DocumentUtil; +import com.arangodb.model.*; +import com.arangodb.util.RawData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import static com.arangodb.internal.serde.SerdeUtils.constructParametricType; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +public class ArangoCollectionAsyncImpl extends InternalArangoCollection implements ArangoCollectionAsync { + + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoCollectionAsyncImpl.class); + + private final ArangoDatabaseAsync db; + + protected ArangoCollectionAsyncImpl(final ArangoDatabaseAsyncImpl db, final String name) { + super(db, db.name(), name); + this.db = db; + } + + @Override + public ArangoDatabaseAsync db() { + return db; + } + + @Override + public CompletableFuture> insertDocument(final Object value) { + return executorAsync().execute(insertDocumentRequest(value, new DocumentCreateOptions()), + constructParametricType(DocumentCreateEntity.class, Void.class)); + } + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture> insertDocument(final T value, final DocumentCreateOptions options) { + return insertDocument(value, options, (Class) value.getClass()); + } + + @Override + public CompletableFuture> insertDocument(final T value, final DocumentCreateOptions options, + final Class type) { + return executorAsync().execute(insertDocumentRequest(value, options), + constructParametricType(DocumentCreateEntity.class, type)); + } + + @Override + public CompletableFuture>> insertDocuments(RawData values) { + return executorAsync() + .execute(insertDocumentsRequest(values, new DocumentCreateOptions()), + insertDocumentsResponseDeserializer(Void.class)); + } + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture>> insertDocuments(RawData values, + DocumentCreateOptions options) { + return executorAsync() + .execute(insertDocumentsRequest(values, options), + insertDocumentsResponseDeserializer((Class) values.getClass())); + } + + @Override + public CompletableFuture>> insertDocuments(final Iterable values) { + return insertDocuments(values, new DocumentCreateOptions()); + } + + @Override + public CompletableFuture>> insertDocuments( + final Iterable values, final DocumentCreateOptions options) { + return executorAsync() + .execute(insertDocumentsRequest(values, options), + insertDocumentsResponseDeserializer(Void.class)); + } + + @Override + public CompletableFuture>> insertDocuments(Iterable values, + DocumentCreateOptions options, + Class type) { + return executorAsync() + .execute(insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer(type)); + } + + @Override + public CompletableFuture importDocuments(final Iterable values) { + return importDocuments(values, new DocumentImportOptions()); + } + + @Override + public CompletableFuture importDocuments(final Iterable values, final DocumentImportOptions options) { + return executorAsync().execute(importDocumentsRequest(values, options), DocumentImportEntity.class); + } + + @Override + public CompletableFuture importDocuments(RawData values) { + return importDocuments(values, new DocumentImportOptions()); + } + + @Override + public CompletableFuture importDocuments(RawData values, DocumentImportOptions options) { + return executorAsync().execute(importDocumentsRequest(values, options), DocumentImportEntity.class); + } + + @Override + public CompletableFuture getDocument(final String key, final Class type) { + return getDocument(key, type, new DocumentReadOptions()); + } + + @Override + public CompletableFuture getDocument(final String key, final Class type, final DocumentReadOptions options) { + DocumentUtil.validateDocumentKey(key); + return executorAsync().execute(getDocumentRequest(key, options), getDocumentResponseDeserializer(type)) + .exceptionally(e -> { + if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { + ArangoDBException arangoDBException = (ArangoDBException) e.getCause(); + + // handle Response: 404, Error: 1655 - transaction not found + if (arangoDBException.getErrorNum() != null && arangoDBException.getErrorNum() == 1655) { + throw (CompletionException) e; + } + + if ((arangoDBException.getResponseCode() != null && (arangoDBException.getResponseCode() == 404 || arangoDBException.getResponseCode() == 304 + || arangoDBException.getResponseCode() == 412))) { + return null; + } + } + throw new CompletionException(e); + }); + } + + @Override + public CompletableFuture> getDocuments(final Iterable keys, final Class type) { + return getDocuments(keys, type, new DocumentReadOptions()); + } + + @Override + public CompletableFuture> getDocuments( + final Iterable keys, final Class type, final DocumentReadOptions options) { + return executorAsync().execute(getDocumentsRequest(keys, options), getDocumentsResponseDeserializer(type)); + } + + @Override + public CompletableFuture> replaceDocument(final String key, final Object value) { + return executorAsync().execute(replaceDocumentRequest(key, value, new DocumentReplaceOptions()), + constructParametricType(DocumentUpdateEntity.class, Void.class)); + } + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture> replaceDocument( + final String key, final T value, final DocumentReplaceOptions options) { + return replaceDocument(key, value, options, (Class) value.getClass()); + } + + @Override + public CompletableFuture> replaceDocument(String key, T value, DocumentReplaceOptions options, + Class type) { + return executorAsync().execute(replaceDocumentRequest(key, value, options), + constructParametricType(DocumentUpdateEntity.class, type)); + } + + @Override + public CompletableFuture>> replaceDocuments(RawData values) { + return executorAsync().execute(replaceDocumentsRequest(values, new DocumentReplaceOptions()), + replaceDocumentsResponseDeserializer(Void.class)); + } + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture>> replaceDocuments(RawData values, + DocumentReplaceOptions options) { + return executorAsync().execute(replaceDocumentsRequest(values, options), + replaceDocumentsResponseDeserializer((Class) values.getClass())); + } + + @Override + public CompletableFuture>> replaceDocuments(final Iterable values) { + return replaceDocuments(values, new DocumentReplaceOptions()); + } + + @Override + public CompletableFuture>> replaceDocuments( + final Iterable values, final DocumentReplaceOptions options) { + return executorAsync().execute(replaceDocumentsRequest(values, options), + replaceDocumentsResponseDeserializer(Void.class)); + } + + @Override + public CompletableFuture>> replaceDocuments(Iterable values, + DocumentReplaceOptions options, + Class type) { + return executorAsync().execute(replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(type)); + } + + @Override + public CompletableFuture> updateDocument(final String key, final Object value) { + return updateDocument(key, value, new DocumentUpdateOptions(), Void.class); + } + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture> updateDocument( + final String key, final T value, final DocumentUpdateOptions options) { + return updateDocument(key, value, options, (Class) value.getClass()); + } + + @Override + public CompletableFuture> updateDocument( + final String key, final Object value, final DocumentUpdateOptions options, final Class returnType) { + return executorAsync().execute(updateDocumentRequest(key, value, options), + constructParametricType(DocumentUpdateEntity.class, returnType)); + } + + @Override + public CompletableFuture>> updateDocuments(RawData values) { + return executorAsync() + .execute(updateDocumentsRequest(values, new DocumentUpdateOptions()), + updateDocumentsResponseDeserializer(Void.class)); + } + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture>> updateDocuments(RawData values, + DocumentUpdateOptions options) { + return executorAsync() + .execute(updateDocumentsRequest(values, options), + updateDocumentsResponseDeserializer((Class) values.getClass())); + } + + @Override + public CompletableFuture>> updateDocuments(final Iterable values) { + return updateDocuments(values, new DocumentUpdateOptions()); + } + + @Override + public CompletableFuture>> updateDocuments( + final Iterable values, final DocumentUpdateOptions options) { + return updateDocuments(values, options, Void.class); + } + + @Override + public CompletableFuture>> updateDocuments( + final Iterable values, final DocumentUpdateOptions options, final Class returnType) { + return executorAsync() + .execute(updateDocumentsRequest(values, options), updateDocumentsResponseDeserializer(returnType)); + } + + @Override + public CompletableFuture> deleteDocument(final String key) { + return deleteDocument(key, new DocumentDeleteOptions()); + } + + @Override + public CompletableFuture> deleteDocument(String key, DocumentDeleteOptions options) { + return deleteDocument(key, options, Void.class); + } + + @Override + public CompletableFuture> deleteDocument( + final String key, final DocumentDeleteOptions options, final Class type) { + return executorAsync().execute(deleteDocumentRequest(key, options), + constructParametricType(DocumentDeleteEntity.class, type)); + } + + @Override + public CompletableFuture>> deleteDocuments(RawData values) { + return executorAsync().execute(deleteDocumentsRequest(values, new DocumentDeleteOptions()), + deleteDocumentsResponseDeserializer(Void.class)); + } + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture>> deleteDocuments(RawData values, + DocumentDeleteOptions options) { + return executorAsync().execute(deleteDocumentsRequest(values, options), + deleteDocumentsResponseDeserializer((Class) values.getClass())); + } + + @Override + public CompletableFuture>> deleteDocuments(final Iterable values) { + return deleteDocuments(values, new DocumentDeleteOptions()); + } + + @Override + public CompletableFuture>> deleteDocuments( + final Iterable values, final DocumentDeleteOptions options) { + return deleteDocuments(values, options, Void.class); + } + + @Override + public CompletableFuture>> deleteDocuments( + final Iterable values, final DocumentDeleteOptions options, final Class type) { + return executorAsync().execute(deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer(type)); + } + + @Override + public CompletableFuture documentExists(final String key) { + return documentExists(key, new DocumentExistsOptions()); + } + + @Override + public CompletableFuture documentExists(final String key, final DocumentExistsOptions options) { + return executorAsync().execute(documentExistsRequest(key, options), Void.class) + .thenApply(Objects::nonNull) + .exceptionally(e -> { + if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { + ArangoDBException arangoDBException = (ArangoDBException) e.getCause(); + + // handle Response: 404, Error: 1655 - transaction not found + if (arangoDBException.getErrorNum() != null && arangoDBException.getErrorNum() == 1655) { + throw (CompletionException) e; + } + + if ((arangoDBException.getResponseCode() != null && (arangoDBException.getResponseCode() == 404 || arangoDBException.getResponseCode() == 304 + || arangoDBException.getResponseCode() == 412))) { + return null; + } + } + throw new CompletionException(e); + }); + } + + @Override + public CompletableFuture getIndex(final String id) { + return executorAsync().execute(getIndexRequest(id), IndexEntity.class); + } + + @Override + public CompletableFuture getInvertedIndex(String id) { + return executorAsync().execute(getIndexRequest(id), InvertedIndexEntity.class); + } + + @Override + public CompletableFuture deleteIndex(final String id) { + return executorAsync().execute(deleteIndexRequest(id), deleteIndexResponseDeserializer()); + } + + @Override + public CompletableFuture ensurePersistentIndex(final Iterable fields, final PersistentIndexOptions options) { + return executorAsync().execute(createPersistentIndexRequest(fields, options), IndexEntity.class); + } + + @Override + public CompletableFuture ensureInvertedIndex(final InvertedIndexOptions options) { + return executorAsync().execute(createInvertedIndexRequest(options), InvertedIndexEntity.class); + } + + @Override + public CompletableFuture ensureGeoIndex(final Iterable fields, final GeoIndexOptions options) { + return executorAsync().execute(createGeoIndexRequest(fields, options), IndexEntity.class); + } + + @Deprecated + @Override + public CompletableFuture ensureFulltextIndex(final Iterable fields, final FulltextIndexOptions options) { + return executorAsync().execute(createFulltextIndexRequest(fields, options), IndexEntity.class); + } + + @Override + public CompletableFuture ensureTtlIndex(final Iterable fields, final TtlIndexOptions options) { + return executorAsync().execute(createTtlIndexRequest(fields, options), IndexEntity.class); + } + + @Override + public CompletableFuture ensureZKDIndex(final Iterable fields, final ZKDIndexOptions options) { + return executorAsync().execute(createZKDIndexRequest(fields, options), IndexEntity.class); + } + + @Override + public CompletableFuture> getIndexes() { + return executorAsync().execute(getIndexesRequest(), getIndexesResponseDeserializer()); + } + + @Override + public CompletableFuture> getInvertedIndexes() { + return executorAsync().execute(getIndexesRequest(), getInvertedIndexesResponseDeserializer()); + } + + @Override + public CompletableFuture exists() { + return getInfo() + .thenApply(Objects::nonNull) + .exceptionally(e -> { + if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e.getCause(); + if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(aEx.getErrorNum())) { + return false; + } + } + throw new CompletionException(e); + }); + } + + @Override + public CompletableFuture truncate() { + return truncate(null); + } + + @Override + public CompletableFuture truncate(CollectionTruncateOptions options) { + return executorAsync().execute(truncateRequest(options), CollectionEntity.class); + } + + @Override + public CompletableFuture count() { + return count(null); + } + + @Override + public CompletableFuture count(CollectionCountOptions options) { + return executorAsync().execute(countRequest(options), CollectionPropertiesEntity.class); + } + + @Override + public CompletableFuture create() { + return db().createCollection(name()); + } + + @Override + public CompletableFuture create(final CollectionCreateOptions options) { + return db().createCollection(name(), options); + } + + @Override + public CompletableFuture drop() { + return executorAsync().execute(dropRequest(null), Void.class); + } + + @Override + public CompletableFuture drop(final boolean isSystem) { + return executorAsync().execute(dropRequest(isSystem), Void.class); + } + + @Override + public CompletableFuture getInfo() { + return executorAsync().execute(getInfoRequest(), CollectionEntity.class); + } + + @Override + public CompletableFuture getProperties() { + return executorAsync().execute(getPropertiesRequest(), CollectionPropertiesEntity.class); + } + + @Override + public CompletableFuture changeProperties(final CollectionPropertiesOptions options) { + return executorAsync().execute(changePropertiesRequest(options), CollectionPropertiesEntity.class); + } + + @Override + public CompletableFuture rename(final String newName) { + return executorAsync().execute(renameRequest(newName), CollectionEntity.class); + } + + @Override + public CompletableFuture getResponsibleShard(final Object value) { + return executorAsync().execute(responsibleShardRequest(value), ShardEntity.class); + } + + @Override + public CompletableFuture getRevision() { + return executorAsync().execute(getRevisionRequest(), CollectionRevisionEntity.class); + } + + @Override + public CompletableFuture grantAccess(final String user, final Permissions permissions) { + return executorAsync().execute(grantAccessRequest(user, permissions), Void.class); + } + + @Override + public CompletableFuture revokeAccess(final String user) { + return executorAsync().execute(grantAccessRequest(user, Permissions.NONE), Void.class); + } + + @Override + public CompletableFuture resetAccess(final String user) { + return executorAsync().execute(resetAccessRequest(user), Void.class); + } + + @Override + public CompletableFuture getPermissions(final String user) { + return executorAsync().execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java index a68f4ad81..7f3422efd 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java @@ -22,6 +22,7 @@ import com.arangodb.ArangoCollection; import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabase; import com.arangodb.entity.*; import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; @@ -41,8 +42,16 @@ public class ArangoCollectionImpl extends InternalArangoCollection implements Ar private static final Logger LOGGER = LoggerFactory.getLogger(ArangoCollectionImpl.class); + private final ArangoDatabase db; + protected ArangoCollectionImpl(final ArangoDatabaseImpl db, final String name) { - super(db, name); + super(db, db.name(), name); + this.db = db; + } + + @Override + public ArangoDatabase db() { + return db; } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index 893c04b71..5702b7232 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -20,11 +20,13 @@ package com.arangodb.internal; +import com.arangodb.ArangoCollectionAsync; import com.arangodb.ArangoDBAsync; import com.arangodb.ArangoDBException; import com.arangodb.ArangoDatabaseAsync; import com.arangodb.entity.*; import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; +import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; import com.arangodb.model.arangosearch.AnalyzerDeleteOptions; import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; @@ -88,10 +90,10 @@ public CompletableFuture> getAccessibleDatabases() { return executorAsync().execute(getAccessibleDatabasesRequest(), getDatabaseResponseDeserializer()); } -// @Override -// public ArangoCollection collection(final String name) { -// return new ArangoCollectionImpl(this, name); -// } + @Override + public ArangoCollectionAsync collection(String name) { + return new ArangoCollectionAsyncImpl(this, name); + } @Override public CompletableFuture createCollection(final String name) { @@ -114,19 +116,19 @@ public CompletableFuture> getCollections(final Coll return executorAsync().execute(getCollectionsRequest(options), getCollectionsResponseDeserializer()); } -// @Override -// public CompletableFuture getIndex(final String id) { -// DocumentUtil.validateIndexId(id); -// final String[] split = id.split("/"); -// return collection(split[0]).getIndex(split[1]); -// } + @Override + public CompletableFuture getIndex(final String id) { + DocumentUtil.validateIndexId(id); + final String[] split = id.split("/"); + return collection(split[0]).getIndex(split[1]); + } -// @Override -// public CompletableFuture deleteIndex(final String id) { -// DocumentUtil.validateIndexId(id); -// final String[] split = id.split("/"); -// return collection(split[0]).deleteIndex(split[1]); -// } + @Override + public CompletableFuture deleteIndex(final String id) { + DocumentUtil.validateIndexId(id); + final String[] split = id.split("/"); + return collection(split[0]).deleteIndex(split[1]); + } @Override public CompletableFuture create() { diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java index 5f6aa2777..d5d18a5f8 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoCollection.java @@ -21,7 +21,6 @@ package com.arangodb.internal; import com.arangodb.ArangoDBException; -import com.arangodb.ArangoDatabase; import com.arangodb.entity.*; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; import com.arangodb.internal.util.DocumentUtil; @@ -60,19 +59,15 @@ public abstract class InternalArangoCollection extends ArangoExecuteable { private static final String TRANSACTION_ID = "x-arango-trx-id"; - private final ArangoDatabase db; + protected final String dbName; protected final String name; - protected InternalArangoCollection(final ArangoDatabaseImpl db, final String name) { - super(db); - this.db = db; + protected InternalArangoCollection(final ArangoExecuteable executeable, final String dbName, final String name) { + super(executeable); + this.dbName = dbName; this.name = name; } - public ArangoDatabase db() { - return db; - } - public String name() { return name; } @@ -97,7 +92,7 @@ protected InternalRequest insertDocumentsRequest(final Iterable values, f private InternalRequest createInsertDocumentRequest(final DocumentCreateOptions options) { final DocumentCreateOptions params = (options != null ? options : new DocumentCreateOptions()); - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_DOCUMENT, name); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_DOCUMENT, name); request.putQueryParam(ArangoRequestParam.WAIT_FOR_SYNC, params.getWaitForSync()); request.putQueryParam(RETURN_NEW, params.getReturnNew()); request.putQueryParam(RETURN_OLD, params.getReturnOld()); @@ -149,7 +144,7 @@ protected InternalRequest importDocumentsRequest(final Iterable values, final protected InternalRequest importDocumentsRequest(final DocumentImportOptions options) { final DocumentImportOptions params = options != null ? options : new DocumentImportOptions(); - return request(db.name(), RequestType.POST, PATH_API_IMPORT).putQueryParam(COLLECTION, name) + return request(dbName, RequestType.POST, PATH_API_IMPORT).putQueryParam(COLLECTION, name) .putQueryParam(ArangoRequestParam.WAIT_FOR_SYNC, params.getWaitForSync()) .putQueryParam("fromPrefix", params.getFromPrefix()).putQueryParam("toPrefix", params.getToPrefix()) .putQueryParam(OVERWRITE, params.getOverwrite()).putQueryParam("onDuplicate", params.getOnDuplicate()) @@ -157,7 +152,7 @@ protected InternalRequest importDocumentsRequest(final DocumentImportOptions opt } protected InternalRequest getDocumentRequest(final String key, final DocumentReadOptions options) { - final InternalRequest request = request(db.name(), RequestType.GET, PATH_API_DOCUMENT, + final InternalRequest request = request(dbName, RequestType.GET, PATH_API_DOCUMENT, DocumentUtil.createDocumentHandle(name, key)); final DocumentReadOptions params = (options != null ? options : new DocumentReadOptions()); request.putHeaderParam(ArangoRequestParam.IF_NONE_MATCH, params.getIfNoneMatch()); @@ -175,7 +170,7 @@ protected ResponseDeserializer getDocumentResponseDeserializer(final Clas protected InternalRequest getDocumentsRequest(final Iterable keys, final DocumentReadOptions options) { final DocumentReadOptions params = (options != null ? options : new DocumentReadOptions()); - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_DOCUMENT, name) + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_DOCUMENT, name) .putQueryParam("onlyget", true) .putHeaderParam(ArangoRequestParam.IF_NONE_MATCH, params.getIfNoneMatch()) .putHeaderParam(ArangoRequestParam.IF_MATCH, params.getIfMatch()).setBody(getSerde().serialize(keys)) @@ -236,7 +231,7 @@ protected InternalRequest replaceDocumentsRequest(final RawData values, final Do private InternalRequest createReplaceDocumentRequest(final DocumentReplaceOptions options, String path) { final DocumentReplaceOptions params = (options != null ? options : new DocumentReplaceOptions()); - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_DOCUMENT, path); + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_DOCUMENT, path); request.putHeaderParam(ArangoRequestParam.IF_MATCH, params.getIfMatch()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); request.putQueryParam(ArangoRequestParam.WAIT_FOR_SYNC, params.getWaitForSync()); @@ -296,7 +291,7 @@ protected InternalRequest updateDocumentsRequest(final RawData values, final Doc private InternalRequest createUpdateDocumentRequest(final DocumentUpdateOptions options, String path) { final DocumentUpdateOptions params = (options != null ? options : new DocumentUpdateOptions()); - final InternalRequest request = request(db.name(), RequestType.PATCH, PATH_API_DOCUMENT, path); + final InternalRequest request = request(dbName, RequestType.PATCH, PATH_API_DOCUMENT, path); request.putHeaderParam(ArangoRequestParam.IF_MATCH, params.getIfMatch()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); request.putQueryParam(ArangoRequestParam.KEEP_NULL, params.getKeepNull()); @@ -356,7 +351,7 @@ protected InternalRequest deleteDocumentsRequest(final RawData docs, final Docum private InternalRequest createDeleteDocumentRequest(final DocumentDeleteOptions options, String path) { final DocumentDeleteOptions params = (options != null ? options : new DocumentDeleteOptions()); - final InternalRequest request = request(db.name(), RequestType.DELETE, PATH_API_DOCUMENT, path); + final InternalRequest request = request(dbName, RequestType.DELETE, PATH_API_DOCUMENT, path); request.putHeaderParam(ArangoRequestParam.IF_MATCH, params.getIfMatch()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); request.putQueryParam(ArangoRequestParam.WAIT_FOR_SYNC, params.getWaitForSync()); @@ -395,7 +390,7 @@ protected ResponseDeserializer>> } protected InternalRequest documentExistsRequest(final String key, final DocumentExistsOptions options) { - final InternalRequest request = request(db.name(), RequestType.HEAD, PATH_API_DOCUMENT, + final InternalRequest request = request(dbName, RequestType.HEAD, PATH_API_DOCUMENT, DocumentUtil.createDocumentHandle(name, key)); final DocumentExistsOptions params = (options != null ? options : new DocumentExistsOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -405,11 +400,11 @@ protected InternalRequest documentExistsRequest(final String key, final Document } protected InternalRequest getIndexRequest(final String id) { - return request(db.name(), RequestType.GET, PATH_API_INDEX, createIndexId(id)); + return request(dbName, RequestType.GET, PATH_API_INDEX, createIndexId(id)); } protected InternalRequest deleteIndexRequest(final String id) { - return request(db.name(), RequestType.DELETE, PATH_API_INDEX, createIndexId(id)); + return request(dbName, RequestType.DELETE, PATH_API_INDEX, createIndexId(id)); } protected ResponseDeserializer deleteIndexResponseDeserializer() { @@ -430,7 +425,7 @@ private String createIndexId(final String id) { protected InternalRequest createPersistentIndexRequest( final Iterable fields, final PersistentIndexOptions options) { - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_INDEX); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); request.setBody(getSerde().serialize( OptionsBuilder.build(options != null ? options : new PersistentIndexOptions(), fields))); @@ -438,14 +433,14 @@ protected InternalRequest createPersistentIndexRequest( } protected InternalRequest createInvertedIndexRequest(final InvertedIndexOptions options) { - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_INDEX); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); request.setBody(getSerde().serialize(options)); return request; } protected InternalRequest createGeoIndexRequest(final Iterable fields, final GeoIndexOptions options) { - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_INDEX); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); request.setBody( getSerde().serialize(OptionsBuilder.build(options != null ? options : new GeoIndexOptions(), fields))); @@ -454,7 +449,7 @@ protected InternalRequest createGeoIndexRequest(final Iterable fields, f @Deprecated protected InternalRequest createFulltextIndexRequest(final Iterable fields, final FulltextIndexOptions options) { - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_INDEX); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); request.setBody( getSerde().serialize(OptionsBuilder.build(options != null ? options : new FulltextIndexOptions(), @@ -463,7 +458,7 @@ protected InternalRequest createFulltextIndexRequest(final Iterable fiel } protected InternalRequest createTtlIndexRequest(final Iterable fields, final TtlIndexOptions options) { - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_INDEX); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); request.setBody( getSerde().serialize(OptionsBuilder.build(options != null ? options : new TtlIndexOptions(), fields))); @@ -472,7 +467,7 @@ protected InternalRequest createTtlIndexRequest(final Iterable fields, f protected InternalRequest createZKDIndexRequest( final Iterable fields, final ZKDIndexOptions options) { - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_INDEX); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); request.setBody(getSerde().serialize(OptionsBuilder.build(options != null ? options : new ZKDIndexOptions().fieldValueTypes(ZKDIndexOptions.FieldValueTypes.DOUBLE), fields))); @@ -480,7 +475,7 @@ protected InternalRequest createZKDIndexRequest( } protected InternalRequest getIndexesRequest() { - final InternalRequest request = request(db.name(), RequestType.GET, PATH_API_INDEX); + final InternalRequest request = request(dbName, RequestType.GET, PATH_API_INDEX); request.putQueryParam(COLLECTION, name); return request; } @@ -510,67 +505,67 @@ protected ResponseDeserializer> getInvertedIndex } protected InternalRequest truncateRequest(final CollectionTruncateOptions options) { - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_COLLECTION, name, "truncate"); + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_COLLECTION, name, "truncate"); final CollectionTruncateOptions params = (options != null ? options : new CollectionTruncateOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); return request; } protected InternalRequest countRequest(final CollectionCountOptions options) { - final InternalRequest request = request(db.name(), RequestType.GET, PATH_API_COLLECTION, name, "count"); + final InternalRequest request = request(dbName, RequestType.GET, PATH_API_COLLECTION, name, "count"); final CollectionCountOptions params = (options != null ? options : new CollectionCountOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); return request; } protected InternalRequest dropRequest(final Boolean isSystem) { - return request(db.name(), RequestType.DELETE, PATH_API_COLLECTION, name).putQueryParam("isSystem", isSystem); + return request(dbName, RequestType.DELETE, PATH_API_COLLECTION, name).putQueryParam("isSystem", isSystem); } protected InternalRequest getInfoRequest() { - return request(db.name(), RequestType.GET, PATH_API_COLLECTION, name); + return request(dbName, RequestType.GET, PATH_API_COLLECTION, name); } protected InternalRequest getPropertiesRequest() { - return request(db.name(), RequestType.GET, PATH_API_COLLECTION, name, "properties"); + return request(dbName, RequestType.GET, PATH_API_COLLECTION, name, "properties"); } protected InternalRequest changePropertiesRequest(final CollectionPropertiesOptions options) { - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_COLLECTION, name, "properties"); + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_COLLECTION, name, "properties"); request.setBody(getSerde().serialize(options != null ? options : new CollectionPropertiesOptions())); return request; } protected InternalRequest renameRequest(final String newName) { - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_COLLECTION, name, "rename"); + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_COLLECTION, name, "rename"); request.setBody(getSerde().serialize(OptionsBuilder.build(new CollectionRenameOptions(), newName))); return request; } protected InternalRequest responsibleShardRequest(final T value) { - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_COLLECTION, name, "responsibleShard"); + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_COLLECTION, name, "responsibleShard"); request.setBody(getSerde().serializeUserData(value)); return request; } protected InternalRequest getRevisionRequest() { - return request(db.name(), RequestType.GET, PATH_API_COLLECTION, name, "revision"); + return request(dbName, RequestType.GET, PATH_API_COLLECTION, name, "revision"); } protected InternalRequest grantAccessRequest(final String user, final Permissions permissions) { return request(ArangoRequestParam.SYSTEM, RequestType.PUT, PATH_API_USER, user, ArangoRequestParam.DATABASE, - db.name(), name).setBody(getSerde().serialize(OptionsBuilder.build(new UserAccessOptions(), + dbName, name).setBody(getSerde().serialize(OptionsBuilder.build(new UserAccessOptions(), permissions))); } protected InternalRequest resetAccessRequest(final String user) { return request(ArangoRequestParam.SYSTEM, RequestType.DELETE, PATH_API_USER, user, ArangoRequestParam.DATABASE, - db.name(), name); + dbName, name); } protected InternalRequest getPermissionsRequest(final String user) { return request(ArangoRequestParam.SYSTEM, RequestType.GET, PATH_API_USER, user, ArangoRequestParam.DATABASE, - db.name(), name); + dbName, name); } protected ResponseDeserializer getPermissionsResponseDeserialzer() { diff --git a/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java b/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java index e35f917cc..2f94f3473 100644 --- a/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java +++ b/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java @@ -26,6 +26,7 @@ import java.io.Closeable; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; /** @@ -40,7 +41,12 @@ default InternalResponse execute(final InternalRequest request, final HostHandle Thread.currentThread().interrupt(); throw ArangoDBException.wrap(e); } catch (ExecutionException e) { - throw ArangoDBException.wrap(e.getCause()); + Throwable cause = e.getCause(); + if (cause instanceof CompletionException) { + throw ArangoDBException.wrap(cause.getCause()); + } else { + throw ArangoDBException.wrap(cause); + } } } diff --git a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java index 14fd49c3a..1b941e7f8 100644 --- a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java @@ -185,17 +185,16 @@ void createDatabaseWithUsers(ArangoDBAsync arangoDB) throws InterruptedException // needed for active-failover tests only Thread.sleep(2_000); - // FIXME -// ArangoDBAsync arangoDBTestUser = new ArangoDB.Builder() -// .loadProperties(config) -// .user("testUser") -// .password("testPasswd") -// .build() -// .async(); - // check if testUser has been created and can access the created db -// ArangoCollectionAsync collection = arangoDBTestUser.db(dbName).collection("col-" + UUID.randomUUID()); -// collection.create().get(); -// arangoDBTestUser.shutdown(); + ArangoDBAsync arangoDBTestUser = new ArangoDB.Builder() + .loadProperties(config) + .user("testUser") + .password("testPasswd") + .build() + .async(); +// check if testUser has been created and can access the created db + ArangoCollectionAsync collection = arangoDBTestUser.db(dbName).collection("col-" + UUID.randomUUID()); + collection.create().get(); + arangoDBTestUser.shutdown(); final Boolean resultDelete = arangoDB.db(dbName).drop().get(); assertThat(resultDelete).isTrue(); @@ -299,7 +298,7 @@ void updateUser(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedEx String username = "user-" + UUID.randomUUID(); final Map extra = new HashMap<>(); extra.put("hund", false); - arangoDB.createUser(username, PW, new UserCreateOptions().extra(extra)); + arangoDB.createUser(username, PW, new UserCreateOptions().extra(extra)).get(); extra.put("hund", true); extra.put("mund", true); final UserEntity user = arangoDB.updateUser(username, new UserUpdateOptions().extra(extra)).get(); diff --git a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java index 13166c82f..a2f869cab 100644 --- a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java @@ -34,10 +34,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; +import java.util.*; import java.util.concurrent.ExecutionException; import static org.assertj.core.api.Assertions.assertThat; @@ -124,8 +121,8 @@ void createCollectionWithReplicationFactor(ArangoDatabaseAsync db) throws Execut .createCollection(name, new CollectionCreateOptions().replicationFactor(2)).get(); assertThat(result).isNotNull(); assertThat(result.getId()).isNotNull(); -// CollectionPropertiesEntity props = db.collection(name).getProperties(); -// assertThat(props.getReplicationFactor().get()).isEqualTo(2); + CollectionPropertiesEntity props = db.collection(name).getProperties().get(); + assertThat(props.getReplicationFactor().get()).isEqualTo(2); } @ParameterizedTest(name = "{index}") @@ -139,9 +136,9 @@ void createCollectionWithWriteConcern(ArangoDatabaseAsync db) throws ExecutionEx new CollectionCreateOptions().replicationFactor(2).writeConcern(2)).get(); assertThat(result).isNotNull(); assertThat(result.getId()).isNotNull(); -// CollectionPropertiesEntity props = db.collection(name).getProperties(); -// assertThat(props.getReplicationFactor().get()).isEqualTo(2); -// assertThat(props.getWriteConcern()).isEqualTo(2); + CollectionPropertiesEntity props = db.collection(name).getProperties().get(); + assertThat(props.getReplicationFactor().get()).isEqualTo(2); + assertThat(props.getWriteConcern()).isEqualTo(2); } @ParameterizedTest(name = "{index}") @@ -156,8 +153,8 @@ void createSatelliteCollection(ArangoDatabaseAsync db) throws ExecutionException assertThat(result).isNotNull(); assertThat(result.getId()).isNotNull(); -// CollectionPropertiesEntity props = db.collection(name).getProperties(); -// assertThat(props.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + CollectionPropertiesEntity props = db.collection(name).getProperties().get(); + assertThat(props.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); } @ParameterizedTest(name = "{index}") @@ -170,8 +167,8 @@ void createCollectionWithNumberOfShards(ArangoDatabaseAsync db) throws Execution assertThat(result).isNotNull(); assertThat(result.getId()).isNotNull(); -// CollectionPropertiesEntity props = db.collection(name).getProperties(); -// assertThat(props.getNumberOfShards()).isEqualTo(2); + CollectionPropertiesEntity props = db.collection(name).getProperties().get(); + assertThat(props.getNumberOfShards()).isEqualTo(2); } @ParameterizedTest(name = "{index}") @@ -186,27 +183,27 @@ void createCollectionWithShardingStrategys(ArangoDatabaseAsync db) throws Execut assertThat(result).isNotNull(); assertThat(result.getId()).isNotNull(); -// CollectionPropertiesEntity props = db.collection(name).getProperties(); -// assertThat(props.getShardingStrategy()).isEqualTo(ShardingStrategy.COMMUNITY_COMPAT.getInternalName()); + CollectionPropertiesEntity props = db.collection(name).getProperties().get(); + assertThat(props.getShardingStrategy()).isEqualTo(ShardingStrategy.COMMUNITY_COMPAT.getInternalName()); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void createCollectionWithSmartJoinAttribute(ArangoDatabaseAsync db) { -// assumeTrue(isAtLeastVersion(3, 5)); -// assumeTrue(isEnterprise()); -// assumeTrue(isCluster()); -// -// String fooName = rndName(); -// db.collection(fooName).create(); -// -// String name = rndName(); -// final CollectionEntity result = db.createCollection(name, -// new CollectionCreateOptions().smartJoinAttribute("test123").distributeShardsLike(fooName).shardKeys("_key:")); -// assertThat(result).isNotNull(); -// assertThat(result.getId()).isNotNull(); -// assertThat(db.collection(name).getProperties().getSmartJoinAttribute()).isEqualTo("test123"); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCollectionWithSmartJoinAttribute(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + + String fooName = rndName(); + db.collection(fooName).create(); + + String name = rndName(); + final CollectionEntity result = db.createCollection(name, + new CollectionCreateOptions().smartJoinAttribute("test123").distributeShardsLike(fooName).shardKeys("_key:")).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + assertThat(db.collection(name).getProperties().get().getSmartJoinAttribute()).isEqualTo("test123"); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -233,9 +230,9 @@ void createCollectionWithNumberOfShardsAndShardKey(ArangoDatabaseAsync db) throw .createCollection(name, new CollectionCreateOptions().numberOfShards(2).shardKeys("a")).get(); assertThat(result).isNotNull(); assertThat(result.getId()).isNotNull(); -// final CollectionPropertiesEntity properties = db.collection(name).getProperties(); -// assertThat(properties.getNumberOfShards()).isEqualTo(2); -// assertThat(properties.getShardKeys()).hasSize(1); + final CollectionPropertiesEntity properties = db.collection(name).getProperties().get(); + assertThat(properties.getNumberOfShards()).isEqualTo(2); + assertThat(properties.getShardKeys()).hasSize(1); } @ParameterizedTest(name = "{index}") @@ -247,9 +244,9 @@ void createCollectionWithNumberOfShardsAndShardKeys(ArangoDatabaseAsync db) thro new CollectionCreateOptions().numberOfShards(2).shardKeys("a", "b")).get(); assertThat(result).isNotNull(); assertThat(result.getId()).isNotNull(); -// final CollectionPropertiesEntity properties = db.collection(name).getProperties(); -// assertThat(properties.getNumberOfShards()).isEqualTo(2); -// assertThat(properties.getShardKeys()).hasSize(2); + final CollectionPropertiesEntity properties = db.collection(name).getProperties().get(); + assertThat(properties.getNumberOfShards()).isEqualTo(2); + assertThat(properties.getShardKeys()).hasSize(2); } @ParameterizedTest(name = "{index}") @@ -265,8 +262,8 @@ void createCollectionWithDistributeShardsLike(ArangoDatabaseAsync db) throws Exe db.createCollection(name1, new CollectionCreateOptions().numberOfShards(numberOfShards)).get(); db.createCollection(name2, new CollectionCreateOptions().distributeShardsLike(name1)).get(); -// assertThat(db.collection(name1).getProperties().getNumberOfShards()).isEqualTo(numberOfShards); -// assertThat(db.collection(name2).getProperties().getNumberOfShards()).isEqualTo(numberOfShards); + assertThat(db.collection(name1).getProperties().get().getNumberOfShards()).isEqualTo(numberOfShards); + assertThat(db.collection(name2).getProperties().get().getNumberOfShards()).isEqualTo(numberOfShards); } private void createCollectionWithKeyType(ArangoDatabaseAsync db, KeyType keyType) throws ExecutionException, InterruptedException { @@ -277,7 +274,7 @@ private void createCollectionWithKeyType(ArangoDatabaseAsync db, KeyType keyType null, null )).get(); -// assertThat(db.collection(name).getProperties().getKeyOptions().getType()).isEqualTo(keyType); + assertThat(db.collection(name).getProperties().get().getKeyOptions().getType()).isEqualTo(keyType); } @ParameterizedTest(name = "{index}") @@ -335,24 +332,24 @@ void createCollectionWithJsonSchema(ArangoDatabaseAsync db) throws ExecutionExce assertThat(result.getSchema().getRule()).isEqualTo(rule); assertThat(result.getSchema().getMessage()).isEqualTo(message); -// CollectionPropertiesEntity props = db.collection(name).getProperties(); -// assertThat(props.getSchema().getLevel()).isEqualTo(CollectionSchema.Level.NEW); -// assertThat(props.getSchema().getRule()).isEqualTo(rule); -// assertThat(props.getSchema().getMessage()).isEqualTo(message); + CollectionPropertiesEntity props = db.collection(name).getProperties().get(); + assertThat(props.getSchema().getLevel()).isEqualTo(CollectionSchema.Level.NEW); + assertThat(props.getSchema().getRule()).isEqualTo(rule); + assertThat(props.getSchema().getMessage()).isEqualTo(message); -// BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); -// doc.addAttribute("number", 33); -// db.collection(name).insertDocument(doc); + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("number", 33); + db.collection(name).insertDocument(doc); -// BaseDocument wrongDoc = new BaseDocument(UUID.randomUUID().toString()); -// wrongDoc.addAttribute("number", "notANumber"); -// Throwable thrown = catchThrowable(() -> db.collection(name).insertDocument(wrongDoc)); -// assertThat(thrown).isInstanceOf(ArangoDBException.class); -// ArangoDBException e = (ArangoDBException) thrown; + BaseDocument wrongDoc = new BaseDocument(UUID.randomUUID().toString()); + wrongDoc.addAttribute("number", "notANumber"); + Throwable thrown = catchThrowable(() -> db.collection(name).insertDocument(wrongDoc).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; -// assertThat(e).hasMessageContaining(message); -// assertThat(e.getResponseCode()).isEqualTo(400); -// assertThat(e.getErrorNum()).isEqualTo(1620); + assertThat(e).hasMessageContaining(message); + assertThat(e.getResponseCode()).isEqualTo(400); + assertThat(e.getErrorNum()).isEqualTo(1620); } @ParameterizedTest(name = "{index}") @@ -383,12 +380,12 @@ void createCollectionWithComputedFields(ArangoDatabaseAsync db) throws Execution .keepNull(true) .failOnWarning(false); -// db.collection(cName).changeProperties(new CollectionPropertiesOptions().computedValues(cv2)); -// -// CollectionPropertiesEntity props = db.collection(cName).getProperties(); -// assertThat(props.getComputedValues()) -// .hasSize(1) -// .contains(cv2); + db.collection(cName).changeProperties(new CollectionPropertiesOptions().computedValues(cv2)).get(); + + CollectionPropertiesEntity props = db.collection(cName).getProperties().get(); + assertThat(props.getComputedValues()) + .hasSize(1) + .contains(cv2); } @ParameterizedTest(name = "{index}") @@ -396,72 +393,63 @@ void createCollectionWithComputedFields(ArangoDatabaseAsync db) throws Execution void deleteCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { String name = rndName(); db.createCollection(name, null).get(); -// db.collection(name).drop(); -// Throwable thrown = catchThrowable(() -> db.collection(name).getInfo()); -// assertThat(thrown).isInstanceOf(ArangoDBException.class); + db.collection(name).drop().get(); + Throwable thrown = catchThrowable(() -> db.collection(name).getInfo().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void deleteSystemCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { -// final String name = "_system_test"; -// db.createCollection(name, new CollectionCreateOptions().isSystem(true)).get(); -// db.collection(name).drop(true); -// Throwable thrown = catchThrowable(() -> db.collection(name).getInfo()); -// assertThat(thrown) -// .isInstanceOf(ArangoDBException.class) -// .extracting(it -> ((ArangoDBException) it).getResponseCode()) -// .isEqualTo(404); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void deleteSystemCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final String name = "_system_test"; + db.createCollection(name, new CollectionCreateOptions().isSystem(true)).get(); + db.collection(name).drop(true).get(); + Throwable thrown = catchThrowable(() -> db.collection(name).getInfo().get()).getCause(); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .extracting(it -> ((ArangoDBException) it).getResponseCode()) + .isEqualTo(404); + } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void deleteSystemCollectionFail(ArangoDatabaseAsync db) { -// final String name = "_system_test"; -// ArangoCollection collection = db.collection(name); -// if (collection.exists()) -// collection.drop(true); -// -// db.createCollection(name, new CollectionCreateOptions().isSystem(true)); -// try { -// collection.drop(); -// fail(); -// } catch (final ArangoDBException e) { -// assertThat(e.getResponseCode()).isEqualTo(403); -// } -// collection.drop(true); -// try { -// collection.getInfo(); -// fail(); -// } catch (final ArangoDBException e) { -// assertThat(e.getResponseCode()).isEqualTo(404); -// } -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void deleteSystemCollectionFail(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final String name = "_system_test"; + ArangoCollectionAsync collection = db.collection(name); + if (collection.exists().get()) + collection.drop(true).get(); + + db.createCollection(name, new CollectionCreateOptions().isSystem(true)).get(); + Throwable thrown = catchThrowable(() -> collection.drop().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(403); + collection.drop(true).get(); + assertThat(collection.exists().get()).isFalse(); + } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void getIndex(ArangoDatabaseAsync db) { -// final Collection fields = Collections.singletonList("field-" + rnd()); -// final IndexEntity createResult = db.collection(CNAME1).ensurePersistentIndex(fields, null); -// final IndexEntity readResult = db.getIndex(createResult.getId()); -// assertThat(readResult.getId()).isEqualTo(createResult.getId()); -// assertThat(readResult.getType()).isEqualTo(createResult.getType()); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getIndex(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Collection fields = Collections.singletonList("field-" + rnd()); + final IndexEntity createResult = db.collection(CNAME1).ensurePersistentIndex(fields, null).get(); + final IndexEntity readResult = db.getIndex(createResult.getId()).get(); + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getType()).isEqualTo(createResult.getType()); + } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void deleteIndex(ArangoDatabaseAsync db) { -// final Collection fields = Collections.singletonList("field-" + rnd()); -// final IndexEntity createResult = db.collection(CNAME1).ensurePersistentIndex(fields, null); -// final String id = db.deleteIndex(createResult.getId()); -// assertThat(id).isEqualTo(createResult.getId()); -// try { -// db.getIndex(id); -// fail(); -// } catch (final ArangoDBException e) { -// assertThat(e.getResponseCode()).isEqualTo(404); -// } -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void deleteIndex(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Collection fields = Collections.singletonList("field-" + rnd()); + final IndexEntity createResult = db.collection(CNAME1).ensurePersistentIndex(fields, null).get(); + final String id = db.deleteIndex(createResult.getId()).get(); + assertThat(id).isEqualTo(createResult.getId()); + Throwable thrown = catchThrowable(() -> db.getIndex(id).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(404); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -779,7 +767,7 @@ void changeQueryCache(ArangoDatabaseAsync db) throws ExecutionException, Interru // properties2.setMode(CacheMode.off); // db.setQueryCacheProperties(properties2); // } - +// // @ParameterizedTest(name = "{index}") // @MethodSource("asyncDbs") // void queryWithMemoryLimit(ArangoDatabaseAsync db) { @@ -1308,10 +1296,10 @@ void createGraphReplicationFaktor(ArangoDatabaseAsync db) throws ExecutionExcept Collections.singletonList(new EdgeDefinition().collection(edgeCollection).from(fromCollection).to(toCollection)); final GraphEntity result = db.createGraph(name, edgeDefinitions, new GraphCreateOptions().replicationFactor(2)).get(); assertThat(result).isNotNull(); -// for (final String collection : Arrays.asList(edgeCollection, fromCollection, toCollection)) { -// final CollectionPropertiesEntity properties = db.collection(collection).getProperties(); -// assertThat(properties.getReplicationFactor().get()).isEqualTo(2); -// } + for (final String collection : Arrays.asList(edgeCollection, fromCollection, toCollection)) { + final CollectionPropertiesEntity properties = db.collection(collection).getProperties().get(); + assertThat(properties.getReplicationFactor().get()).isEqualTo(2); + } } @ParameterizedTest(name = "{index}") @@ -1327,10 +1315,10 @@ void createGraphNumberOfShards(ArangoDatabaseAsync db) throws ExecutionException final GraphEntity result = db .createGraph(name, edgeDefinitions, new GraphCreateOptions().numberOfShards(2)).get(); assertThat(result).isNotNull(); -// for (final String collection : Arrays.asList(edgeCollection, fromCollection, toCollection)) { -// final CollectionPropertiesEntity properties = db.collection(collection).getProperties(); -// assertThat(properties.getNumberOfShards()).isEqualTo(2); -// } + for (final String collection : Arrays.asList(edgeCollection, fromCollection, toCollection)) { + final CollectionPropertiesEntity properties = db.collection(collection).getProperties().get(); + assertThat(properties.getNumberOfShards()).isEqualTo(2); + } } @ParameterizedTest(name = "{index}") @@ -1423,32 +1411,32 @@ void transactionCollection(ArangoDatabaseAsync db) throws ExecutionException, In assertThat(result.get()).isEqualTo("\"hello world\""); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void transactionInsertJson(ArangoDatabaseAsync db) { -// String key = "key-" + rnd(); -// final TransactionOptions options = new TransactionOptions().params("{\"_key\":\"" + key + "\"}") -// .writeCollections(CNAME1); -// db.transaction("function (params) { " -// + "var db = require('internal').db;" -// + "db." + CNAME1 + ".save(JSON.parse(params));" -// + "}", Void.class, options); -// assertThat(db.collection(CNAME1).getDocument(key, RawJson.class)).isNotNull(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void transactionExclusiveWrite(ArangoDatabaseAsync db) { -// assumeTrue(isAtLeastVersion(3, 4)); -// String key = "key-" + rnd(); -// final TransactionOptions options = new TransactionOptions().params("{\"_key\":\"" + key + "\"}") -// .exclusiveCollections(CNAME1); -// db.transaction("function (params) { " -// + "var db = require('internal').db;" -// + "db." + CNAME1 + ".save(JSON.parse(params));" -// + "}", Void.class, options); -// assertThat(db.collection(CNAME1).getDocument(key, RawJson.class)).isNotNull(); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionInsertJson(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + String key = "key-" + rnd(); + final TransactionOptions options = new TransactionOptions().params("{\"_key\":\"" + key + "\"}") + .writeCollections(CNAME1); + db.transaction("function (params) { " + + "var db = require('internal').db;" + + "db." + CNAME1 + ".save(JSON.parse(params));" + + "}", Void.class, options).get(); + assertThat(db.collection(CNAME1).getDocument(key, RawJson.class).get()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionExclusiveWrite(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String key = "key-" + rnd(); + final TransactionOptions options = new TransactionOptions().params("{\"_key\":\"" + key + "\"}") + .exclusiveCollections(CNAME1); + db.transaction("function (params) { " + + "var db = require('internal').db;" + + "db." + CNAME1 + ".save(JSON.parse(params));" + + "}", Void.class, options).get(); + assertThat(db.collection(CNAME1).getDocument(key, RawJson.class).get()).isNotNull(); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") From 4215d04bf6f06672c6289b930f5e544ad751e324 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 13 Oct 2023 11:12:47 +0200 Subject: [PATCH 21/62] ArangoCollectionAsyncTest --- .../internal/ArangoCollectionAsyncImpl.java | 54 +- .../arangodb/ArangoCollectionAsyncTest.java | 3214 +++++++++++++++++ 2 files changed, 3235 insertions(+), 33 deletions(-) create mode 100644 driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java index 5caa4acd2..445bb9168 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java @@ -140,22 +140,7 @@ public CompletableFuture getDocument(final String key, final Class typ public CompletableFuture getDocument(final String key, final Class type, final DocumentReadOptions options) { DocumentUtil.validateDocumentKey(key); return executorAsync().execute(getDocumentRequest(key, options), getDocumentResponseDeserializer(type)) - .exceptionally(e -> { - if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { - ArangoDBException arangoDBException = (ArangoDBException) e.getCause(); - - // handle Response: 404, Error: 1655 - transaction not found - if (arangoDBException.getErrorNum() != null && arangoDBException.getErrorNum() == 1655) { - throw (CompletionException) e; - } - - if ((arangoDBException.getResponseCode() != null && (arangoDBException.getResponseCode() == 404 || arangoDBException.getResponseCode() == 304 - || arangoDBException.getResponseCode() == 412))) { - return null; - } - } - throw new CompletionException(e); - }); + .exceptionally(this::catchGetDocumentExceptions); } @Override @@ -331,23 +316,26 @@ public CompletableFuture documentExists(final String key) { @Override public CompletableFuture documentExists(final String key, final DocumentExistsOptions options) { return executorAsync().execute(documentExistsRequest(key, options), Void.class) - .thenApply(Objects::nonNull) - .exceptionally(e -> { - if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { - ArangoDBException arangoDBException = (ArangoDBException) e.getCause(); - - // handle Response: 404, Error: 1655 - transaction not found - if (arangoDBException.getErrorNum() != null && arangoDBException.getErrorNum() == 1655) { - throw (CompletionException) e; - } - - if ((arangoDBException.getResponseCode() != null && (arangoDBException.getResponseCode() == 404 || arangoDBException.getResponseCode() == 304 - || arangoDBException.getResponseCode() == 412))) { - return null; - } - } - throw new CompletionException(e); - }); + .thenApply(it -> true) + .exceptionally(this::catchGetDocumentExceptions) + .thenApply(Objects::nonNull); + } + + T catchGetDocumentExceptions(Throwable e) { + if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { + ArangoDBException arangoDBException = (ArangoDBException) e.getCause(); + + // handle Response: 404, Error: 1655 - transaction not found + if (arangoDBException.getErrorNum() != null && arangoDBException.getErrorNum() == 1655) { + throw (CompletionException) e; + } + + if ((arangoDBException.getResponseCode() != null && (arangoDBException.getResponseCode() == 404 || arangoDBException.getResponseCode() == 304 + || arangoDBException.getResponseCode() == 412))) { + return null; + } + } + throw new CompletionException(e); } @Override diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java new file mode 100644 index 000000000..37baa6ef0 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java @@ -0,0 +1,3214 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.internal.serde.SerdeUtils; +import com.arangodb.model.*; +import com.arangodb.model.DocumentImportOptions.OnDuplicate; +import com.arangodb.serde.jackson.Id; +import com.arangodb.serde.jackson.JacksonSerde; +import com.arangodb.serde.jackson.Key; +import com.arangodb.serde.jackson.Rev; +import com.arangodb.util.MapBuilder; +import com.arangodb.util.RawBytes; +import com.arangodb.util.RawData; +import com.arangodb.util.RawJson; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoCollectionAsyncTest extends BaseJunit5 { + + private static final String COLLECTION_NAME = "ArangoCollectionTest_collection"; + private static final String EDGE_COLLECTION_NAME = "ArangoCollectionTest_edge_collection"; + + private final ObjectMapper mapper = new ObjectMapper(); + + private static Stream asyncCols() { + return asyncDbsStream().map(db -> db.collection(COLLECTION_NAME)).map(Arguments::of); + } + + private static Stream edges() { + return dbsStream().map(db -> db.collection(EDGE_COLLECTION_NAME)).map(Arguments::of); + } + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + initEdgeCollections(EDGE_COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocument(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateEntity doc = collection.insertDocument(new BaseDocument(), null).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNull(); + assertThat(doc.getId()).isEqualTo(COLLECTION_NAME + "/" + doc.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentWithArrayWithNullValues(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + List arr = Arrays.asList("a", null); + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("arr", arr); + + final DocumentCreateEntity insertedDoc = collection.insertDocument(doc, + new DocumentCreateOptions().returnNew(true)).get(); + assertThat(insertedDoc).isNotNull(); + assertThat(insertedDoc.getId()).isNotNull(); + assertThat(insertedDoc.getKey()).isNotNull(); + assertThat(insertedDoc.getRev()).isNotNull(); + assertThat(insertedDoc.getId()).isEqualTo(COLLECTION_NAME + "/" + insertedDoc.getKey()); + //noinspection unchecked + assertThat((List) insertedDoc.getNew().getAttribute("arr")).containsAll(Arrays.asList("a", null)); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentWithNullValues(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("null", null); + + final DocumentCreateEntity insertedDoc = collection.insertDocument(doc, + new DocumentCreateOptions().returnNew(true)).get(); + assertThat(insertedDoc).isNotNull(); + assertThat(insertedDoc.getId()).isNotNull(); + assertThat(insertedDoc.getKey()).isNotNull(); + assertThat(insertedDoc.getRev()).isNotNull(); + assertThat(insertedDoc.getId()).isEqualTo(COLLECTION_NAME + "/" + insertedDoc.getKey()); + assertThat(insertedDoc.getNew().getProperties()).containsKey("null"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentUpdateRev(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateOptions options = new DocumentCreateOptions().returnNew(true); + final DocumentCreateEntity doc = collection.insertDocument(new BaseDocument(), options).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentWithTypeOverwriteModeReplace(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + assumeTrue(collection.getSerde().getUserSerde() instanceof JacksonSerde, "polymorphic deserialization support" + + " required"); + + String key = UUID.randomUUID().toString(); + Dog dog = new Dog(key, "Teddy"); + Cat cat = new Cat(key, "Luna"); + + final DocumentCreateOptions options = new DocumentCreateOptions() + .returnNew(true) + .returnOld(true) + .overwriteMode(OverwriteMode.replace); + collection.insertDocument(dog, options); + final DocumentCreateEntity doc = collection.insertDocument(cat, options, Animal.class).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull().isEqualTo(key); + assertThat(doc.getRev()).isNotNull(); + + assertThat(doc.getOld()) + .isNotNull() + .isInstanceOf(Dog.class); + assertThat(doc.getOld().getKey()).isEqualTo(key); + assertThat(doc.getOld().getName()).isEqualTo("Teddy"); + + assertThat(doc.getNew()) + .isNotNull() + .isInstanceOf(Cat.class); + assertThat(doc.getNew().getKey()).isEqualTo(key); + assertThat(doc.getNew().getName()).isEqualTo("Luna"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentOverwriteModeIgnore(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + + String key = "key-" + UUID.randomUUID(); + final BaseDocument doc = new BaseDocument(key); + doc.addAttribute("foo", "a"); + final DocumentCreateEntity meta = collection.insertDocument(doc).get(); + + final BaseDocument doc2 = new BaseDocument(key); + doc2.addAttribute("bar", "b"); + final DocumentCreateEntity insertIgnore = collection.insertDocument(doc2, + new DocumentCreateOptions().overwriteMode(OverwriteMode.ignore)).get(); + + assertThat(insertIgnore).isNotNull(); + assertThat(insertIgnore.getRev()).isEqualTo(meta.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentOverwriteModeConflict(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + + String key = "key-" + UUID.randomUUID(); + final BaseDocument doc = new BaseDocument(key); + doc.addAttribute("foo", "a"); + collection.insertDocument(doc).get(); + + final BaseDocument doc2 = new BaseDocument(key); + Throwable thrown = catchThrowable(() -> collection.insertDocument(doc2, + new DocumentCreateOptions().overwriteMode(OverwriteMode.conflict)).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1210); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentOverwriteModeReplace(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + + String key = "key-" + UUID.randomUUID(); + final BaseDocument doc = new BaseDocument(key); + doc.addAttribute("foo", "a"); + final DocumentCreateEntity meta = collection.insertDocument(doc).get(); + + final BaseDocument doc2 = new BaseDocument(key); + doc2.addAttribute("bar", "b"); + final DocumentCreateEntity repsert = collection.insertDocument(doc2, + new DocumentCreateOptions().overwriteMode(OverwriteMode.replace).returnNew(true)).get(); + + assertThat(repsert).isNotNull(); + assertThat(repsert.getRev()).isNotEqualTo(meta.getRev()); + assertThat(repsert.getNew().getProperties().containsKey("foo")).isFalse(); + assertThat(repsert.getNew().getAttribute("bar")).isEqualTo("b"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentOverwriteModeUpdate(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("foo", "a"); + final DocumentCreateEntity meta = collection.insertDocument(doc).get(); + + doc.addAttribute("bar", "b"); + final DocumentCreateEntity updated = collection.insertDocument(doc, + new DocumentCreateOptions().overwriteMode(OverwriteMode.update).returnNew(true)).get(); + + assertThat(updated).isNotNull(); + assertThat(updated.getRev()).isNotEqualTo(meta.getRev()); + assertThat(updated.getNew().getAttribute("foo")).isEqualTo("a"); + assertThat(updated.getNew().getAttribute("bar")).isEqualTo("b"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentOverwriteModeUpdateMergeObjectsFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + Map fieldA = Collections.singletonMap("a", "a"); + doc.addAttribute("foo", fieldA); + final DocumentCreateEntity meta = collection.insertDocument(doc).get(); + + Map fieldB = Collections.singletonMap("b", "b"); + doc.addAttribute("foo", fieldB); + final DocumentCreateEntity updated = collection.insertDocument(doc, + new DocumentCreateOptions().overwriteMode(OverwriteMode.update).mergeObjects(false).returnNew(true)).get(); + + assertThat(updated).isNotNull(); + assertThat(updated.getRev()).isNotEqualTo(meta.getRev()); + assertThat(updated.getNew().getAttribute("foo")).isEqualTo(fieldB); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentOverwriteModeUpdateKeepNullTrue(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("foo", "bar"); + collection.insertDocument(doc); + + doc.updateAttribute("foo", null); + final BaseDocument updated = collection.insertDocument(doc, new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .keepNull(true) + .returnNew(true)).get().getNew(); + + assertThat(updated.getProperties()).containsEntry("foo", null); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentOverwriteModeUpdateKeepNullFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("foo", "bar"); + collection.insertDocument(doc).get(); + + doc.updateAttribute("foo", null); + final BaseDocument updated = collection.insertDocument(doc, new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .keepNull(false) + .returnNew(true)).get().getNew(); + + assertThat(updated.getProperties()).doesNotContainKey("foo"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentWaitForSync(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateOptions options = new DocumentCreateOptions().waitForSync(true); + final DocumentCreateEntity doc = collection.insertDocument(new BaseDocument(), options).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentRefillIndexCaches(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateOptions options = new DocumentCreateOptions().refillIndexCaches(true); + final DocumentCreateEntity doc = collection.insertDocument(new BaseDocument(), options).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentAsJson(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String key = "doc-" + UUID.randomUUID(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\",\"a\":\"test\"}"); + final DocumentCreateEntity doc = collection.insertDocument(rawJson).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isEqualTo(collection.name() + "/" + key); + assertThat(doc.getKey()).isEqualTo(key); + assertThat(doc.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentAsBytes(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String key = "doc-" + UUID.randomUUID(); + Map doc = new HashMap<>(); + doc.put("_key", key); + doc.put("a", "test"); + byte[] bytes = collection.getSerde().serializeUserData(doc); + RawBytes rawJson = RawBytes.of(bytes); + final DocumentCreateEntity createEntity = collection.insertDocument(rawJson, + new DocumentCreateOptions().returnNew(true)).get(); + assertThat(createEntity).isNotNull(); + assertThat(createEntity.getId()).isEqualTo(collection.name() + "/" + key); + assertThat(createEntity.getKey()).isEqualTo(key); + assertThat(createEntity.getRev()).isNotNull(); + assertThat(createEntity.getNew()).isNotNull().isInstanceOf(RawBytes.class); + Map newDoc = collection.getSerde().deserializeUserData(createEntity.getNew().get(), + Map.class); + assertThat(newDoc).containsAllEntriesOf(doc); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentSilent(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final DocumentCreateEntity meta = collection.insertDocument(new BaseDocument(), + new DocumentCreateOptions().silent(true)).get(); + assertThat(meta).isNotNull(); + assertThat(meta.getId()).isNull(); + assertThat(meta.getKey()).isNull(); + assertThat(meta.getRev()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentSilentDontTouchInstance(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final String key = "testkey-" + UUID.randomUUID(); + doc.setKey(key); + final DocumentCreateEntity meta = collection.insertDocument(doc, + new DocumentCreateOptions().silent(true)).get(); + assertThat(meta).isNotNull(); + assertThat(meta.getKey()).isNull(); + assertThat(doc.getKey()).isEqualTo(key); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsSilent(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final MultiDocumentEntity> info = + collection.insertDocuments(Arrays.asList(new BaseDocument(), new BaseDocument()), + new DocumentCreateOptions().silent(true), BaseDocument.class).get(); + assertThat(info).isNotNull(); + assertThat(info.getDocuments()).isEmpty(); + assertThat(info.getDocumentsAndErrors()).isEmpty(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsRefillIndexCaches(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final MultiDocumentEntity> info = + collection.insertDocuments(Arrays.asList(new BaseDocument(), new BaseDocument()), + new DocumentCreateOptions().refillIndexCaches(true), BaseDocument.class).get(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocument(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentIfMatch(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifMatch(createResult.getRev()); + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, options).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentIfMatchFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifMatch("no"); + final BaseDocument document = collection.getDocument(createResult.getKey(), BaseDocument.class, options).get(); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentIfNoneMatch(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifNoneMatch("no"); + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, options).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentIfNoneMatchFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifNoneMatch(createResult.getRev()); + final BaseDocument document = collection.getDocument(createResult.getKey(), BaseDocument.class, options).get(); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentAsJson(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\",\"a\":\"test\"}"); + collection.insertDocument(rawJson).get(); + final RawJson readResult = collection.getDocument(key, RawJson.class).get(); + assertThat(readResult.get()).contains("\"_key\":\"" + key + "\"").contains("\"_id\":\"" + COLLECTION_NAME + "/" + key + "\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentNotFound(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument document = collection.getDocument("no", BaseDocument.class).get(); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentNotFoundOptionsDefault(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument document = collection.getDocument("no", BaseDocument.class, new DocumentReadOptions()).get(); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentNotFoundOptionsNull(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument document = collection.getDocument("no", BaseDocument.class, null).get(); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentWrongKey(ArangoCollectionAsync collection) { + Throwable thrown = catchThrowable(() -> collection.getDocument("no/no", BaseDocument.class).get()); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentDirtyRead(ArangoCollectionAsync collection) throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + collection.insertDocument(doc, new DocumentCreateOptions()); + Thread.sleep(2000); + final RawJson document = collection.getDocument(doc.getKey(), RawJson.class, + new DocumentReadOptions().allowDirtyRead(true)).get(); + assertThat(document).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocuments(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("3")); + collection.insertDocuments(values).get(); + final MultiDocumentEntity documents = collection.getDocuments(Arrays.asList("1", "2", "3"), + BaseDocument.class).get(); + assertThat(documents).isNotNull(); + assertThat(documents.getDocuments()).hasSize(3); + for (final BaseDocument document : documents.getDocuments()) { + assertThat(document.getId()).isIn(COLLECTION_NAME + "/" + "1", COLLECTION_NAME + "/" + "2", + COLLECTION_NAME + "/" + "3"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentsWithCustomShardingKey(ArangoCollectionAsync c) throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = c.db().collection("customShardingKeyCollection"); + if (collection.exists().get()) collection.drop(); + + collection.create(new CollectionCreateOptions().shardKeys("customField").numberOfShards(10)).get(); + + List values = + IntStream.range(0, 10).mapToObj(String::valueOf).map(key -> new BaseDocument()).peek(it -> it.addAttribute( + "customField", rnd())).collect(Collectors.toList()); + + MultiDocumentEntity> inserted = collection.insertDocuments(values).get(); + List insertedKeys = + inserted.getDocuments().stream().map(DocumentEntity::getKey).collect(Collectors.toList()); + + final Collection documents = + collection.getDocuments(insertedKeys, BaseDocument.class).get().getDocuments(); + + assertThat(documents).hasSize(10); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentsDirtyRead(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("3")); + collection.insertDocuments(values); + final MultiDocumentEntity documents = collection.getDocuments(Arrays.asList("1", "2", "3"), + BaseDocument.class, new DocumentReadOptions().allowDirtyRead(true)).get(); + assertThat(documents).isNotNull(); + if (isAtLeastVersion(3, 10)) { + assertThat(documents.isPotentialDirtyRead()).isTrue(); + } + assertThat(documents.getDocuments()).hasSize(3); + for (final BaseDocument document : documents.getDocuments()) { + assertThat(document.getId()).isIn(COLLECTION_NAME + "/" + "1", COLLECTION_NAME + "/" + "2", + COLLECTION_NAME + "/" + "3"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentsNotFound(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final MultiDocumentEntity readResult = collection.getDocuments(Collections.singleton("no"), + BaseDocument.class).get(); + assertThat(readResult).isNotNull(); + assertThat(readResult.getDocuments()).isEmpty(); + assertThat(readResult.getErrors()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getDocumentsWrongKey(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final MultiDocumentEntity readResult = collection.getDocuments(Collections.singleton("no/no"), + BaseDocument.class).get(); + assertThat(readResult).isNotNull(); + assertThat(readResult.getDocuments()).isEmpty(); + assertThat(readResult.getErrors()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocument(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + null).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getNew()).isNull(); + assertThat(updateResult.getOld()).isNull(); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getAttribute("a")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("a"))).isEqualTo("test1"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + assertThat(readResult.getRevision()).isEqualTo(updateResult.getRev()); + assertThat(readResult.getProperties()).containsKey("c"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentWithDifferentReturnType(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final String key = "key-" + UUID.randomUUID(); + final BaseDocument doc = new BaseDocument(key); + doc.addAttribute("a", "test"); + collection.insertDocument(doc); + + final DocumentUpdateEntity updateResult = collection.updateDocument(key, + Collections.singletonMap("b", "test"), new DocumentUpdateOptions().returnNew(true), BaseDocument.class).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getKey()).isEqualTo(key); + BaseDocument updated = updateResult.getNew(); + assertThat(updated).isNotNull(); + assertThat(updated.getAttribute("a")).isEqualTo("test"); + assertThat(updated.getAttribute("b")).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentUpdateRev(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.addAttribute("foo", "bar"); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + null).get(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(updateResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentIfMatch(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final DocumentUpdateOptions options = new DocumentUpdateOptions().ifMatch(createResult.getRev()); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getAttribute("a")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("a"))).isEqualTo("test1"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + assertThat(readResult.getRevision()).isEqualTo(updateResult.getRev()); + assertThat(readResult.getProperties()).containsKey("c"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentIfMatchFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + + final DocumentUpdateOptions options = new DocumentUpdateOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> collection.updateDocument(createResult.getKey(), doc, options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + final DocumentUpdateOptions options = new DocumentUpdateOptions().returnNew(true); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + assertThat(updateResult.getNew()).isNotNull(); + assertThat(updateResult.getNew().getKey()).isEqualTo(createResult.getKey()); + assertThat(updateResult.getNew().getRevision()).isNotEqualTo(createResult.getRev()); + assertThat(updateResult.getNew().getAttribute("a")).isNotNull(); + assertThat(String.valueOf(updateResult.getNew().getAttribute("a"))).isEqualTo("test1"); + assertThat(updateResult.getNew().getAttribute("b")).isNotNull(); + assertThat(String.valueOf(updateResult.getNew().getAttribute("b"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentReturnOld(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + final DocumentUpdateOptions options = new DocumentUpdateOptions().returnOld(true); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + assertThat(updateResult.getOld()).isNotNull(); + assertThat(updateResult.getOld().getKey()).isEqualTo(createResult.getKey()); + assertThat(updateResult.getOld().getRevision()).isEqualTo(createResult.getRev()); + assertThat(updateResult.getOld().getAttribute("a")).isNotNull(); + assertThat(String.valueOf(updateResult.getOld().getAttribute("a"))).isEqualTo("test"); + assertThat(updateResult.getOld().getProperties().keySet()).doesNotContain("b"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentKeepNullTrue(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.updateAttribute("a", null); + final DocumentUpdateOptions options = new DocumentUpdateOptions().keepNull(true); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentKeepNullFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.updateAttribute("a", null); + final DocumentUpdateOptions options = new DocumentUpdateOptions().keepNull(false); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getRevision()).isNotNull(); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentSerializeNullTrue(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final TestUpdateEntity doc = new TestUpdateEntity(); + doc.a = "foo"; + doc.b = "foo"; + final DocumentCreateEntity createResult = collection.insertDocument(doc).get(); + final TestUpdateEntity patchDoc = new TestUpdateEntity(); + patchDoc.a = "bar"; + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), patchDoc).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getKey()).isEqualTo(createResult.getKey()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties()).containsKey("a"); + assertThat(readResult.getAttribute("a")).isEqualTo("bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentSerializeNullFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final TestUpdateEntitySerializeNullFalse doc = new TestUpdateEntitySerializeNullFalse(); + doc.a = "foo"; + doc.b = "foo"; + final DocumentCreateEntity createResult = collection.insertDocument(doc).get(); + final TestUpdateEntitySerializeNullFalse patchDoc = new TestUpdateEntitySerializeNullFalse(); + patchDoc.a = "bar"; + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), patchDoc).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getKey()).isEqualTo(createResult.getKey()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties()).containsKeys("a", "b"); + assertThat(readResult.getAttribute("a")).isEqualTo("bar"); + assertThat(readResult.getAttribute("b")).isEqualTo("foo"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentMergeObjectsTrue(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final Map a = new HashMap<>(); + a.put("a", "test"); + doc.addAttribute("a", a); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + a.clear(); + a.put("b", "test"); + doc.updateAttribute("a", a); + final DocumentUpdateOptions options = new DocumentUpdateOptions().mergeObjects(true); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + final Object aResult = readResult.getAttribute("a"); + assertThat(aResult).isInstanceOf(Map.class); + final Map aMap = (Map) aResult; + assertThat(aMap).containsKeys("a", "b"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentMergeObjectsFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final Map a = new HashMap<>(); + a.put("a", "test"); + doc.addAttribute("a", a); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + a.clear(); + a.put("b", "test"); + doc.updateAttribute("a", a); + final DocumentUpdateOptions options = new DocumentUpdateOptions().mergeObjects(false); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + final Object aResult = readResult.getAttribute("a"); + assertThat(aResult).isInstanceOf(Map.class); + final Map aMap = (Map) aResult; + assertThat(aMap.keySet()).doesNotContain("a"); + assertThat(aMap).containsKey("b"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentIgnoreRevsFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.setRevision("no"); + + final DocumentUpdateOptions options = new DocumentUpdateOptions().ignoreRevs(false); + Throwable thrown = catchThrowable(() -> collection.updateDocument(createResult.getKey(), doc, options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentSilent(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final DocumentUpdateEntity meta = collection.updateDocument(createResult.getKey(), + new BaseDocument(), new DocumentUpdateOptions().silent(true)).get(); + assertThat(meta).isNotNull(); + assertThat(meta.getId()).isNull(); + assertThat(meta.getKey()).isNull(); + assertThat(meta.getRev()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsSilent(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final MultiDocumentEntity> info = + collection.updateDocuments(Collections.singletonList(new BaseDocument(createResult.getKey())), + new DocumentUpdateOptions().silent(true), BaseDocument.class).get(); + assertThat(info).isNotNull(); + assertThat(info.getDocuments()).isEmpty(); + assertThat(info.getDocumentsAndErrors()).isEmpty(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateNonExistingDocument(ArangoCollectionAsync collection) { + final BaseDocument doc = new BaseDocument("test-" + rnd()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + + Throwable thrown = catchThrowable(() -> collection.updateDocument(doc.getKey(), doc, null).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(404); + assertThat(e.getErrorNum()).isEqualTo(1202); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentPreconditionFailed(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument("test-" + rnd()); + doc.addAttribute("foo", "a"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + + doc.updateAttribute("foo", "b"); + collection.updateDocument(doc.getKey(), doc, null).get(); + + doc.updateAttribute("foo", "c"); + Throwable thrown = catchThrowable(() -> collection.updateDocument(doc.getKey(), doc, + new DocumentUpdateOptions().ifMatch(createResult.getRev())).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + BaseDocument readDocument = collection.getDocument(doc.getKey(), BaseDocument.class).get(); + assertThat(readDocument.getAttribute("foo")).isEqualTo("b"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentRefillIndexCaches(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + BaseDocument doc = new BaseDocument(); + DocumentCreateEntity createResult = collection.insertDocument(doc).get(); + doc.addAttribute("foo", "bar"); + DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), + doc, new DocumentUpdateOptions().refillIndexCaches(true)).get(); + assertThat(updateResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsRefillIndexCaches(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final MultiDocumentEntity> info = + collection.updateDocuments(Collections.singletonList(new BaseDocument(createResult.getKey())), + new DocumentUpdateOptions().refillIndexCaches(true), BaseDocument.class).get(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocument(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), + doc, null).get(); + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getNew()).isNull(); + assertThat(replaceResult.getOld()).isNull(); + assertThat(replaceResult.getRev()).isNotEqualTo(replaceResult.getOldRev()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getRevision()).isEqualTo(replaceResult.getRev()); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentUpdateRev(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), + doc, null).get(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(replaceResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentIfMatch(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().ifMatch(createResult.getRev()); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), + doc, options).get(); + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getRev()).isNotEqualTo(replaceResult.getOldRev()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getRevision()).isEqualTo(replaceResult.getRev()); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentIfMatchFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + + final DocumentReplaceOptions options = new DocumentReplaceOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> collection.replaceDocument(createResult.getKey(), doc, options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentIgnoreRevsFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + doc.setRevision("no"); + + final DocumentReplaceOptions options = new DocumentReplaceOptions().ignoreRevs(false); + Throwable thrown = catchThrowable(() -> collection.replaceDocument(createResult.getKey(), doc, options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().returnNew(true); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), + doc, options).get(); + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + assertThat(replaceResult.getNew()).isNotNull(); + assertThat(replaceResult.getNew().getKey()).isEqualTo(createResult.getKey()); + assertThat(replaceResult.getNew().getRevision()).isNotEqualTo(createResult.getRev()); + assertThat(replaceResult.getNew().getProperties().keySet()).doesNotContain("a"); + assertThat(replaceResult.getNew().getAttribute("b")).isNotNull(); + assertThat(String.valueOf(replaceResult.getNew().getAttribute("b"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentReturnOld(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().returnOld(true); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), + doc, options).get(); + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + assertThat(replaceResult.getOld()).isNotNull(); + assertThat(replaceResult.getOld().getKey()).isEqualTo(createResult.getKey()); + assertThat(replaceResult.getOld().getRevision()).isEqualTo(createResult.getRev()); + assertThat(replaceResult.getOld().getAttribute("a")).isNotNull(); + assertThat(String.valueOf(replaceResult.getOld().getAttribute("a"))).isEqualTo("test"); + assertThat(replaceResult.getOld().getProperties().keySet()).doesNotContain("b"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentSilent(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final DocumentUpdateEntity meta = collection.replaceDocument(createResult.getKey(), + new BaseDocument(), new DocumentReplaceOptions().silent(true)).get(); + assertThat(meta).isNotNull(); + assertThat(meta.getId()).isNull(); + assertThat(meta.getKey()).isNull(); + assertThat(meta.getRev()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentSilentDontTouchInstance(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc).get(); + final DocumentUpdateEntity meta = collection.replaceDocument(createResult.getKey(), doc, + new DocumentReplaceOptions().silent(true)).get(); + assertThat(meta.getRev()).isNull(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentsSilent(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final MultiDocumentEntity> info = + collection.replaceDocuments(Collections.singletonList(new BaseDocument(createResult.getKey())), + new DocumentReplaceOptions().silent(true), BaseDocument.class).get(); + assertThat(info).isNotNull(); + assertThat(info.getDocuments()).isEmpty(); + assertThat(info.getDocumentsAndErrors()).isEmpty(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentRefillIndexCaches(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc).get(); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), doc, + new DocumentReplaceOptions().refillIndexCaches(true)).get(); + assertThat(replaceResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentsRefillIndexCaches(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final MultiDocumentEntity> info = + collection.replaceDocuments(Collections.singletonList(new BaseDocument(createResult.getKey())), + new DocumentReplaceOptions().refillIndexCaches(true), BaseDocument.class).get(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocument(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + collection.deleteDocument(createResult.getKey()).get(); + final BaseDocument document = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentReturnOld(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + final DocumentDeleteOptions options = new DocumentDeleteOptions().returnOld(true); + final DocumentDeleteEntity deleteResult = collection.deleteDocument(createResult.getKey(), + options, BaseDocument.class).get(); + assertThat(deleteResult.getOld()).isNotNull(); + assertThat(deleteResult.getOld()).isInstanceOf(BaseDocument.class); + assertThat(deleteResult.getOld().getAttribute("a")).isNotNull(); + assertThat(String.valueOf(deleteResult.getOld().getAttribute("a"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentIfMatch(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null).get(); + final DocumentDeleteOptions options = new DocumentDeleteOptions().ifMatch(createResult.getRev()); + collection.deleteDocument(createResult.getKey(), options).get(); + final BaseDocument document = collection.getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentIfMatchFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc).get(); + final DocumentDeleteOptions options = new DocumentDeleteOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> collection.deleteDocument(createResult.getKey(), options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentSilent(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final DocumentDeleteEntity meta = collection.deleteDocument(createResult.getKey(), + new DocumentDeleteOptions().silent(true), BaseDocument.class).get(); + assertThat(meta).isNotNull(); + assertThat(meta.getId()).isNull(); + assertThat(meta.getKey()).isNull(); + assertThat(meta.getRev()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsSilent(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final MultiDocumentEntity> info = collection.deleteDocuments( + Collections.singletonList(createResult.getKey()), + new DocumentDeleteOptions().silent(true), + BaseDocument.class).get(); + assertThat(info).isNotNull(); + assertThat(info.getDocuments()).isEmpty(); + assertThat(info.getDocumentsAndErrors()).isEmpty(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentRefillIndexCaches(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + DocumentDeleteEntity deleteResult = collection.deleteDocument(createResult.getKey(), + new DocumentDeleteOptions().refillIndexCaches(true)).get(); + assertThat(deleteResult.getRev()) + .isNotNull() + .isEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsRefillIndexCaches(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()).get(); + final MultiDocumentEntity> info = collection.deleteDocuments( + Collections.singletonList(createResult.getKey()), + new DocumentDeleteOptions().refillIndexCaches(true), + BaseDocument.class).get(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getIndex(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = collection.ensurePersistentIndex(fields, null).get(); + final IndexEntity readResult = collection.getIndex(createResult.getId()).get(); + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getType()).isEqualTo(createResult.getType()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getIndexByKey(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = collection.ensurePersistentIndex(fields, null).get(); + final IndexEntity readResult = collection.getIndex(createResult.getId().split("/")[1]).get(); + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getType()).isEqualTo(createResult.getType()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteIndex(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = collection.ensurePersistentIndex(fields, null).get(); + final String id = collection.deleteIndex(createResult.getId()).get(); + assertThat(id).isEqualTo(createResult.getId()); + Throwable thrown = catchThrowable(() -> collection.db().getIndex(id).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteIndexByKey(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = collection.ensurePersistentIndex(fields, null).get(); + final String id = collection.deleteIndex(createResult.getId().split("/")[1]).get(); + assertThat(id).isEqualTo(createResult.getId()); + Throwable thrown = catchThrowable(() -> collection.db().getIndex(id).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createGeoIndex(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + final IndexEntity indexResult = collection.ensureGeoIndex(fields, null).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getUnique()).isFalse(); + if (isAtLeastVersion(3, 4)) { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo); + } else { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo1); + } + if (isAtLeastVersion(3, 10)) { + assertThat(indexResult.getLegacyPolygons()).isFalse(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createGeoIndexWithOptions(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "geoIndex-" + rnd(); + final GeoIndexOptions options = new GeoIndexOptions(); + options.name(name); + + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + final IndexEntity indexResult = collection.ensureGeoIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getUnique()).isFalse(); + if (isAtLeastVersion(3, 4)) { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo); + } else { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo1); + } + assertThat(indexResult.getName()).isEqualTo(name); + if (isAtLeastVersion(3, 10)) { + assertThat(indexResult.getLegacyPolygons()).isFalse(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createGeoIndexLegacyPolygons(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + String name = "geoIndex-" + rnd(); + final GeoIndexOptions options = new GeoIndexOptions(); + options.name(name); + options.legacyPolygons(true); + + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + final IndexEntity indexResult = collection.ensureGeoIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getUnique()).isFalse(); + if (isAtLeastVersion(3, 4)) { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo); + } else { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo1); + } + assertThat(indexResult.getName()).isEqualTo(name); + if (isAtLeastVersion(3, 10)) { + assertThat(indexResult.getLegacyPolygons()).isTrue(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createGeo2Index(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensureGeoIndex(fields, null).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getFields()).contains(f2); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getUnique()).isFalse(); + if (isAtLeastVersion(3, 4)) { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo); + } else { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo2); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createGeo2IndexWithOptions(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "geoIndex-" + rnd(); + final GeoIndexOptions options = new GeoIndexOptions(); + options.name(name); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensureGeoIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getFields()).contains(f2); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getUnique()).isFalse(); + if (isAtLeastVersion(3, 4)) { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo); + } else { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo2); + } + assertThat(indexResult.getName()).isEqualTo(name); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createPersistentIndex(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, null).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getFields()).contains(f2); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isFalse(); + assertThat(indexResult.getType()).isEqualTo(IndexType.persistent); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getDeduplicate()).isTrue(); + if (isAtLeastVersion(3, 10)) { + assertThat(indexResult.getCacheEnabled()).isFalse(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createPersistentIndexCacheEnabled(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, new PersistentIndexOptions().cacheEnabled(true)).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getFields()).contains(f2); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isFalse(); + assertThat(indexResult.getType()).isEqualTo(IndexType.persistent); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getDeduplicate()).isTrue(); + assertThat(indexResult.getCacheEnabled()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createPersistentIndexStoredValues(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, new PersistentIndexOptions().storedValues("v1", "v2")).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getFields()).contains(f2); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isFalse(); + assertThat(indexResult.getType()).isEqualTo(IndexType.persistent); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getDeduplicate()).isTrue(); + assertThat(indexResult.getCacheEnabled()).isFalse(); + assertThat(indexResult.getStoredValues()) + .hasSize(2) + .contains("v1", "v2"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createPersistentIndexWithOptions(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "persistentIndex-" + rnd(); + final PersistentIndexOptions options = new PersistentIndexOptions(); + options.name(name); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getFields()).contains(f2); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isFalse(); + assertThat(indexResult.getType()).isEqualTo(IndexType.persistent); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getName()).isEqualTo(name); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createZKDIndex(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 9)); + collection.truncate(); + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensureZKDIndex(fields, null).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getFields()).contains(f2); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getType()).isEqualTo(IndexType.zkd); + assertThat(indexResult.getUnique()).isFalse(); + collection.deleteIndex(indexResult.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createZKDIndexWithOptions(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 9)); + collection.truncate(); + + String name = "ZKDIndex-" + rnd(); + final ZKDIndexOptions options = + new ZKDIndexOptions().name(name).fieldValueTypes(ZKDIndexOptions.FieldValueTypes.DOUBLE); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensureZKDIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getFields()).contains(f2); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getType()).isEqualTo(IndexType.zkd); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getName()).isEqualTo(name); + collection.deleteIndex(indexResult.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void indexEstimates(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + assumeTrue(isSingleServer()); + + String name = "persistentIndex-" + rnd(); + final PersistentIndexOptions options = new PersistentIndexOptions(); + options.name(name); + options.estimates(true); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getEstimates()).isTrue(); + assertThat(indexResult.getSelectivityEstimate()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void indexEstimatesFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + assumeTrue(isSingleServer()); + + String name = "persistentIndex-" + rnd(); + final PersistentIndexOptions options = new PersistentIndexOptions(); + options.name(name); + options.estimates(false); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getEstimates()).isFalse(); + assertThat(indexResult.getSelectivityEstimate()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void indexDeduplicate(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + + String name = "persistentIndex-" + rnd(); + final PersistentIndexOptions options = new PersistentIndexOptions(); + options.name(name); + options.deduplicate(true); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getDeduplicate()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void indexDeduplicateFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + + String name = "persistentIndex-" + rnd(); + final PersistentIndexOptions options = new PersistentIndexOptions(); + options.name(name); + options.deduplicate(false); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getDeduplicate()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createFulltextIndex(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + final IndexEntity indexResult = collection.ensureFulltextIndex(fields, null).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getType()).isEqualTo(IndexType.fulltext); + assertThat(indexResult.getUnique()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createFulltextIndexWithOptions(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "fulltextIndex-" + rnd(); + final FulltextIndexOptions options = new FulltextIndexOptions(); + options.name(name); + + String f = "field-" + rnd(); + final Collection fields = Collections.singletonList(f); + final IndexEntity indexResult = collection.ensureFulltextIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains(f); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getType()).isEqualTo(IndexType.fulltext); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getName()).isEqualTo(name); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createTtlIndexWithoutOptions(ArangoCollectionAsync collection) { + assumeTrue(isAtLeastVersion(3, 5)); + final Collection fields = new ArrayList<>(); + fields.add("a"); + + Throwable thrown = catchThrowable(() -> collection.ensureTtlIndex(fields, null).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(400); + assertThat(e.getErrorNum()).isEqualTo(10); + assertThat(e.getMessage()).contains("expireAfter attribute must be a number"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createTtlIndexWithOptions(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + + String name = "ttlIndex-" + rnd(); + final TtlIndexOptions options = new TtlIndexOptions(); + options.name(name); + options.expireAfter(3600); + + final IndexEntity indexResult = collection.ensureTtlIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains(f1); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getType()).isEqualTo(IndexType.ttl); + assertThat(indexResult.getExpireAfter()).isEqualTo(3600); + assertThat(indexResult.getName()).isEqualTo(name); + + // revert changes + collection.deleteIndex(indexResult.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getIndexes(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + collection.ensurePersistentIndex(fields, null).get(); + long matchingIndexes = + collection.getIndexes().get().stream().filter(i -> i.getType() == IndexType.persistent).filter(i -> i.getFields().contains(f1)).count(); + assertThat(matchingIndexes).isEqualTo(1L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("edges") + void getEdgeIndex(ArangoCollection edgeCollection) { + Collection indexes = edgeCollection.getIndexes(); + long primaryIndexes = indexes.stream().filter(i -> i.getType() == IndexType.primary).count(); + long edgeIndexes = indexes.stream().filter(i -> i.getType() == IndexType.primary).count(); + assertThat(primaryIndexes).isEqualTo(1L); + assertThat(edgeIndexes).isEqualTo(1L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void exists(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assertThat(collection.exists().get()).isTrue(); + assertThat(collection.db().collection(COLLECTION_NAME + "no").exists().get()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void truncate(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + collection.insertDocument(doc, null).get(); + final BaseDocument readResult = collection.getDocument(doc.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(doc.getKey()); + final CollectionEntity truncateResult = collection.truncate().get(); + assertThat(truncateResult).isNotNull(); + assertThat(truncateResult.getId()).isNotNull(); + final BaseDocument document = collection.getDocument(doc.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getCount(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + Long initialCount = collection.count().get().getCount(); + collection.insertDocument(RawJson.of("{}")).get(); + final CollectionPropertiesEntity count = collection.count().get(); + assertThat(count.getCount()).isEqualTo(initialCount + 1L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void documentExists(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Boolean existsNot = collection.documentExists(rnd(), null).get(); + assertThat(existsNot).isFalse(); + + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + collection.insertDocument(rawJson).get(); + final Boolean exists = collection.documentExists(key, null).get(); + assertThat(exists).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void documentExistsIfMatch(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + final DocumentCreateEntity createResult = collection.insertDocument(rawJson).get(); + final DocumentExistsOptions options = new DocumentExistsOptions().ifMatch(createResult.getRev()); + final Boolean exists = collection.documentExists(key, options).get(); + assertThat(exists).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void documentExistsIfMatchFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + collection.insertDocument(rawJson).get(); + final DocumentExistsOptions options = new DocumentExistsOptions().ifMatch("no"); + final Boolean exists = collection.documentExists(key, options).get(); + assertThat(exists).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void documentExistsIfNoneMatch(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + collection.insertDocument(rawJson).get(); + final DocumentExistsOptions options = new DocumentExistsOptions().ifNoneMatch("no"); + final Boolean exists = collection.documentExists(key, options).get(); + assertThat(exists).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void documentExistsIfNoneMatchFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + final DocumentCreateEntity createResult = collection.insertDocument(rawJson).get(); + final DocumentExistsOptions options = new DocumentExistsOptions().ifNoneMatch(createResult.getRev()); + final Boolean exists = collection.documentExists(key, options).get(); + assertThat(exists).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocuments(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = Arrays.asList(new BaseDocument(), new BaseDocument(), + new BaseDocument()); + + final MultiDocumentEntity docs = collection.insertDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsOverwriteModeUpdate(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 7)); + + final BaseDocument doc1 = new BaseDocument(UUID.randomUUID().toString()); + doc1.addAttribute("foo", "a"); + final DocumentCreateEntity meta1 = collection.insertDocument(doc1).get(); + + final BaseDocument doc2 = new BaseDocument(UUID.randomUUID().toString()); + doc2.addAttribute("foo", "a"); + final DocumentCreateEntity meta2 = collection.insertDocument(doc2).get(); + + doc1.addAttribute("bar", "b"); + doc2.addAttribute("bar", "b"); + + final MultiDocumentEntity> repsert = + collection.insertDocuments(Arrays.asList(doc1, doc2), + new DocumentCreateOptions().overwriteMode(OverwriteMode.update).returnNew(true), BaseDocument.class).get(); + assertThat(repsert).isNotNull(); + assertThat(repsert.getDocuments()).hasSize(2); + assertThat(repsert.getErrors()).isEmpty(); + for (final DocumentCreateEntity documentCreateEntity : repsert.getDocuments()) { + assertThat(documentCreateEntity.getRev()).isNotEqualTo(meta1.getRev()); + assertThat(documentCreateEntity.getRev()).isNotEqualTo(meta2.getRev()); + assertThat(documentCreateEntity.getNew().getAttribute("foo")).isEqualTo("a"); + assertThat(documentCreateEntity.getNew().getAttribute("bar")).isEqualTo("b"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsJson(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + values.add(RawJson.of("{}")); + values.add(RawJson.of("{}")); + values.add(RawJson.of("{}")); + final MultiDocumentEntity docs = collection.insertDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsRawData(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final RawData values = RawJson.of("[{},{},{}]"); + final MultiDocumentEntity docs = collection.insertDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsRawDataReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final RawData values = RawJson.of("[{\"aaa\":33},{\"aaa\":33},{\"aaa\":33}]"); + final MultiDocumentEntity> docs = + collection.insertDocuments(values, new DocumentCreateOptions().returnNew(true)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + + for (final DocumentCreateEntity doc : docs.getDocuments()) { + RawData d = doc.getNew(); + assertThat(d) + .isNotNull() + .isInstanceOf(RawJson.class); + + JsonNode jn = SerdeUtils.INSTANCE.parseJson(((RawJson) d).get()); + assertThat(jn.has("aaa")).isTrue(); + assertThat(jn.get("aaa").intValue()).isEqualTo(33); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsOne(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + final MultiDocumentEntity docs = collection.insertDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(1); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsEmpty(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + final MultiDocumentEntity docs = collection.insertDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).isEmpty(); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + final DocumentCreateOptions options = new DocumentCreateOptions().returnNew(true); + final MultiDocumentEntity> docs = collection.insertDocuments(values, + options, BaseDocument.class).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + for (final DocumentCreateEntity doc : docs.getDocuments()) { + assertThat(doc.getNew()).isNotNull(); + final BaseDocument baseDocument = doc.getNew(); + assertThat(baseDocument.getKey()).isNotNull(); + } + + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void insertDocumentsFail(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + final Collection values = Arrays.asList(new BaseDocument(k1), new BaseDocument(k2), + new BaseDocument(k2)); + + final MultiDocumentEntity docs = collection.insertDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(2); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).hasSize(1); + assertThat(docs.getErrors().iterator().next().getErrorNum()).isEqualTo(1210); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocuments(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = Arrays.asList(new BaseDocument(), new BaseDocument(), + new BaseDocument()); + + final DocumentImportEntity docs = collection.importDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(values.size()); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonList(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = Arrays.asList( + RawJson.of("{}"), + RawJson.of("{}"), + RawJson.of("{}") + ); + + final DocumentImportEntity docs = collection.importDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(values.size()); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsDuplicateDefaultError(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final Collection values = Arrays.asList(new BaseDocument(k1), new BaseDocument(k2), + new BaseDocument(k2)); + + final DocumentImportEntity docs = collection.importDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsDuplicateError(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final Collection values = Arrays.asList(new BaseDocument(k1), new BaseDocument(k2), + new BaseDocument(k2)); + + final DocumentImportEntity docs = collection.importDocuments(values, + new DocumentImportOptions().onDuplicate(OnDuplicate.error)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsDuplicateIgnore(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final Collection values = Arrays.asList(new BaseDocument(k1), new BaseDocument(k2), + new BaseDocument(k2)); + + final DocumentImportEntity docs = collection.importDocuments(values, + new DocumentImportOptions().onDuplicate(OnDuplicate.ignore)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isEqualTo(1); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsDuplicateReplace(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final Collection values = Arrays.asList(new BaseDocument(k1), new BaseDocument(k2), + new BaseDocument(k2)); + + final DocumentImportEntity docs = collection.importDocuments(values, + new DocumentImportOptions().onDuplicate(OnDuplicate.replace)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isEqualTo(1); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsDuplicateUpdate(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final Collection values = Arrays.asList(new BaseDocument(k1), new BaseDocument(k2), + new BaseDocument(k2)); + + final DocumentImportEntity docs = collection.importDocuments(values, + new DocumentImportOptions().onDuplicate(OnDuplicate.update)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isEqualTo(1); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsCompleteFail(ArangoCollectionAsync collection) { + String k1 = rnd(); + String k2 = rnd(); + + final Collection values = Arrays.asList(new BaseDocument(k1), new BaseDocument(k2), + new BaseDocument(k2)); + + Throwable thrown = catchThrowable(() -> collection.importDocuments(values, + new DocumentImportOptions().complete(true)).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getErrorNum()).isEqualTo(1210); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsDetails(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final Collection values = Arrays.asList(new BaseDocument(k1), new BaseDocument(k2), + new BaseDocument(k2)); + + final DocumentImportEntity docs = collection.importDocuments(values, new DocumentImportOptions().details(true)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).hasSize(1); + assertThat(docs.getDetails().iterator().next()).contains("unique constraint violated"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsOverwriteFalse(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + collection.insertDocument(new BaseDocument()).get(); + Long initialCount = collection.count().get().getCount(); + + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + collection.importDocuments(values, new DocumentImportOptions().overwrite(false)).get(); + assertThat(collection.count().get().getCount()).isEqualTo(initialCount + 2L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsOverwriteTrue(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + collection.insertDocument(new BaseDocument()).get(); + + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + collection.importDocuments(values, new DocumentImportOptions().overwrite(true)).get(); + assertThat(collection.count().get().getCount()).isEqualTo(2L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("edges") + void importDocumentsFromToPrefix(ArangoCollection edgeCollection) { + final Collection values = new ArrayList<>(); + final String[] keys = {rnd(), rnd()}; + for (String s : keys) { + values.add(new BaseEdgeDocument(s, "from", "to")); + } + assertThat(values).hasSize(keys.length); + + final DocumentImportEntity importResult = edgeCollection.importDocuments(values, + new DocumentImportOptions().fromPrefix("foo").toPrefix("bar")); + assertThat(importResult).isNotNull(); + assertThat(importResult.getCreated()).isEqualTo(values.size()); + for (String key : keys) { + final BaseEdgeDocument doc = edgeCollection.getDocument(key, BaseEdgeDocument.class); + assertThat(doc).isNotNull(); + assertThat(doc.getFrom()).isEqualTo("foo/from"); + assertThat(doc.getTo()).isEqualTo("bar/to"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJson(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", rnd()), + Collections.singletonMap("_key", rnd()))); + + final DocumentImportEntity docs = collection.importDocuments(RawJson.of(values)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonDuplicateDefaultError(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", k1), + Collections.singletonMap("_key", k2), Collections.singletonMap("_key", k2))); + + final DocumentImportEntity docs = collection.importDocuments(RawJson.of(values)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonDuplicateError(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", k1), + Collections.singletonMap("_key", k2), Collections.singletonMap("_key", k2))); + + final DocumentImportEntity docs = collection.importDocuments(RawJson.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.error)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonDuplicateIgnore(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", k1), + Collections.singletonMap("_key", k2), Collections.singletonMap("_key", k2))); + final DocumentImportEntity docs = collection.importDocuments(RawJson.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.ignore)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isEqualTo(1); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonDuplicateReplace(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", k1), + Collections.singletonMap("_key", k2), Collections.singletonMap("_key", k2))); + + final DocumentImportEntity docs = collection.importDocuments(RawJson.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.replace)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isEqualTo(1); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonDuplicateUpdate(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", k1), + Collections.singletonMap("_key", k2), Collections.singletonMap("_key", k2))); + + final DocumentImportEntity docs = collection.importDocuments(RawJson.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.update)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isZero(); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isEqualTo(1); + assertThat(docs.getDetails()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonCompleteFail(ArangoCollectionAsync collection) { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + Throwable thrown = catchThrowable(() -> collection.importDocuments(RawJson.of(values), + new DocumentImportOptions().complete(true)).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getErrorNum()).isEqualTo(1210); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonDetails(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + String k1 = rnd(); + String k2 = rnd(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", k1), + Collections.singletonMap("_key", k2), Collections.singletonMap("_key", k2))); + + final DocumentImportEntity docs = collection.importDocuments(RawJson.of(values), + new DocumentImportOptions().details(true)).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isZero(); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isZero(); + assertThat(docs.getUpdated()).isZero(); + assertThat(docs.getDetails()).hasSize(1); + assertThat(docs.getDetails().iterator().next()).contains("unique constraint violated"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonOverwriteFalse(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + collection.insertDocument(new BaseDocument()).get(); + Long initialCount = collection.count().get().getCount(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", rnd()), + Collections.singletonMap("_key", rnd()))); + collection.importDocuments(RawJson.of(values), new DocumentImportOptions().overwrite(false)).get(); + assertThat(collection.count().get().getCount()).isEqualTo(initialCount + 2L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void importDocumentsJsonOverwriteTrue(ArangoCollectionAsync collection) throws JsonProcessingException, ExecutionException, InterruptedException { + collection.insertDocument(new BaseDocument()).get(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", rnd()), + Collections.singletonMap("_key", rnd()))); + collection.importDocuments(RawJson.of(values), new DocumentImportOptions().overwrite(true)).get(); + assertThat(collection.count().get().getCount()).isEqualTo(2L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("edges") + void importDocumentsJsonFromToPrefix(ArangoCollection edgeCollection) throws JsonProcessingException { + String k1 = UUID.randomUUID().toString(); + String k2 = UUID.randomUUID().toString(); + + final String[] keys = {k1, k2}; + + final String values = mapper.writeValueAsString(Arrays.asList(new MapBuilder().put("_key", k1).put("_from", + "from").put("_to", "to").get(), new MapBuilder().put("_key", k2).put("_from", "from").put("_to", "to").get())); + + final DocumentImportEntity importResult = edgeCollection.importDocuments(RawJson.of(values), + new DocumentImportOptions().fromPrefix("foo").toPrefix("bar")); + assertThat(importResult).isNotNull(); + assertThat(importResult.getCreated()).isEqualTo(2); + for (String key : keys) { + final BaseEdgeDocument doc = edgeCollection.getDocument(key, BaseEdgeDocument.class); + assertThat(doc).isNotNull(); + assertThat(doc.getFrom()).isEqualTo("foo/from"); + assertThat(doc.getTo()).isEqualTo("bar/to"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsByKey(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("2"); + values.add(e); + } + collection.insertDocuments(values).get(); + final Collection keys = new ArrayList<>(); + keys.add("1"); + keys.add("2"); + final MultiDocumentEntity> deleteResult = collection.deleteDocuments(keys).get(); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).hasSize(2); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isIn("1", "2"); + } + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsRawDataByKeyReturnOld(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final RawData values = RawJson.of("[{\"_key\":\"1\"},{\"_key\":\"2\"}]"); + collection.insertDocuments(values).get(); + final RawData keys = RawJson.of("[\"1\",\"2\"]"); + MultiDocumentEntity> deleteResult = collection.deleteDocuments(keys, + new DocumentDeleteOptions().returnOld(true)).get(); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).hasSize(2); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isIn("1", "2"); + assertThat(i.getOld()).isNotNull().isInstanceOf(RawJson.class); + JsonNode jn = SerdeUtils.INSTANCE.parseJson(((RawJson) i.getOld()).get()); + assertThat(jn.get("_key").asText()).isEqualTo(i.getKey()); + } + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsByDocuments(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("2"); + values.add(e); + } + collection.insertDocuments(values).get(); + MultiDocumentEntity> deleteResult = collection.deleteDocuments(values).get(); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).hasSize(2); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isIn("1", "2"); + } + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsByKeyOne(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + collection.insertDocuments(values).get(); + final Collection keys = new ArrayList<>(); + keys.add("1"); + final MultiDocumentEntity> deleteResult = collection.deleteDocuments(keys).get(); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).hasSize(1); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isEqualTo("1"); + } + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsByDocumentOne(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + collection.insertDocuments(values).get(); + final MultiDocumentEntity> deleteResult = collection.deleteDocuments(values).get(); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).hasSize(1); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isEqualTo("1"); + } + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsEmpty(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + collection.insertDocuments(values); + final Collection keys = new ArrayList<>(); + final MultiDocumentEntity deleteResult = collection.deleteDocuments(keys).get(); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).isEmpty(); + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsByKeyNotExisting(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + collection.insertDocuments(values); + final Collection keys = Arrays.asList(rnd(), rnd()); + + final MultiDocumentEntity deleteResult = collection.deleteDocuments(keys).get(); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).isEmpty(); + assertThat(deleteResult.getErrors()).hasSize(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void deleteDocumentsByDocumentsNotExisting(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("2"); + values.add(e); + } + final MultiDocumentEntity deleteResult = collection.deleteDocuments(values).get(); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).isEmpty(); + assertThat(deleteResult.getErrors()).hasSize(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocuments(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = Arrays.asList(new BaseDocument(rnd()), new BaseDocument(rnd())); + collection.insertDocuments(values).get(); + values.forEach(it -> it.addAttribute("a", "test")); + + final MultiDocumentEntity updateResult = collection.updateDocuments(values).get(); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsWithDifferentReturnType(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + List keys = + IntStream.range(0, 3).mapToObj(it -> "key-" + UUID.randomUUID()).collect(Collectors.toList()); + List docs = + keys.stream().map(BaseDocument::new).peek(it -> it.addAttribute("a", "test")).collect(Collectors.toList()); + + collection.insertDocuments(docs).get(); + + List> modifiedDocs = docs.stream().peek(it -> it.addAttribute("b", "test")).map(it -> { + Map map = new HashMap<>(); + map.put("_key", it.getKey()); + map.put("a", it.getAttribute("a")); + map.put("b", it.getAttribute("b")); + return map; + }).collect(Collectors.toList()); + + final MultiDocumentEntity> updateResult = + collection.updateDocuments(modifiedDocs, new DocumentUpdateOptions().returnNew(true), BaseDocument.class).get(); + assertThat(updateResult.getDocuments()).hasSize(3); + assertThat(updateResult.getErrors()).isEmpty(); + assertThat(updateResult.getDocuments().stream()).map(DocumentUpdateEntity::getNew).allMatch(it -> it.getAttribute("a").equals("test")).allMatch(it -> it.getAttribute("b").equals("test")); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsOne(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + collection.insertDocuments(values).get(); + final Collection updatedValues = new ArrayList<>(); + final BaseDocument first = values.iterator().next(); + first.addAttribute("a", "test"); + updatedValues.add(first); + final MultiDocumentEntity updateResult = collection.updateDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(1); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsEmpty(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + final MultiDocumentEntity updateResult = collection.updateDocuments(values).get(); + assertThat(updateResult.getDocuments()).isEmpty(); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsWithoutKey(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + values.add(new BaseDocument("1")); + } + collection.insertDocuments(values); + final Collection updatedValues = new ArrayList<>(); + for (final BaseDocument i : values) { + i.addAttribute("a", "test"); + updatedValues.add(i); + } + updatedValues.add(new BaseDocument()); + final MultiDocumentEntity updateResult = collection.updateDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(1); + assertThat(updateResult.getErrors()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsJson(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + values.add(RawJson.of("{\"_key\":\"1\"}")); + values.add(RawJson.of("{\"_key\":\"2\"}")); + collection.insertDocuments(values); + + final Collection updatedValues = new ArrayList<>(); + updatedValues.add(RawJson.of("{\"_key\":\"1\", \"foo\":\"bar\"}")); + updatedValues.add(RawJson.of("{\"_key\":\"2\", \"foo\":\"bar\"}")); + final MultiDocumentEntity updateResult = collection.updateDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsRawData(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final RawData values = RawJson.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values); + + final RawData updatedValues = RawJson.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + + "\"foo\":\"bar\"}]"); + final MultiDocumentEntity updateResult = collection.updateDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void updateDocumentsRawDataReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final RawData values = RawJson.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values).get(); + + final RawData updatedValues = RawJson.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + + "\"foo\":\"bar\"}]"); + MultiDocumentEntity> updateResult = + collection.updateDocuments(updatedValues, new DocumentUpdateOptions().returnNew(true)).get(); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + for (DocumentUpdateEntity doc : updateResult.getDocuments()) { + RawData d = doc.getNew(); + assertThat(d) + .isNotNull() + .isInstanceOf(RawJson.class); + + JsonNode jn = SerdeUtils.INSTANCE.parseJson(((RawJson) d).get()); + assertThat(jn.has("foo")).isTrue(); + assertThat(jn.get("foo").textValue()).isEqualTo("bar"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocuments(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + } + collection.insertDocuments(values).get(); + final Collection updatedValues = new ArrayList<>(); + for (final BaseDocument i : values) { + i.addAttribute("a", "test"); + updatedValues.add(i); + } + final MultiDocumentEntity updateResult = collection.replaceDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentsOne(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + collection.insertDocuments(values); + final Collection updatedValues = new ArrayList<>(); + final BaseDocument first = values.iterator().next(); + first.addAttribute("a", "test"); + updatedValues.add(first); + final MultiDocumentEntity updateResult = collection.updateDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(1); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentsEmpty(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + final MultiDocumentEntity updateResult = collection.updateDocuments(values).get(); + assertThat(updateResult.getDocuments()).isEmpty(); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentsWithoutKey(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + { + values.add(new BaseDocument("1")); + } + collection.insertDocuments(values); + final Collection updatedValues = new ArrayList<>(); + for (final BaseDocument i : values) { + i.addAttribute("a", "test"); + updatedValues.add(i); + } + updatedValues.add(new BaseDocument()); + final MultiDocumentEntity updateResult = collection.updateDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(1); + assertThat(updateResult.getErrors()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentsJson(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final Collection values = new ArrayList<>(); + values.add(RawJson.of("{\"_key\":\"1\"}")); + values.add(RawJson.of("{\"_key\":\"2\"}")); + collection.insertDocuments(values).get(); + + final Collection updatedValues = new ArrayList<>(); + updatedValues.add(RawJson.of("{\"_key\":\"1\", \"foo\":\"bar\"}")); + updatedValues.add(RawJson.of("{\"_key\":\"2\", \"foo\":\"bar\"}")); + final MultiDocumentEntity updateResult = collection.replaceDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentsRawData(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final RawData values = RawJson.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values); + + final RawData updatedValues = RawJson.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + + "\"foo\":\"bar\"}]"); + final MultiDocumentEntity updateResult = collection.replaceDocuments(updatedValues).get(); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void replaceDocumentsRawDataReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final RawData values = RawJson.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values); + + final RawData updatedValues = RawJson.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + + "\"foo\":\"bar\"}]"); + MultiDocumentEntity> updateResult = + collection.replaceDocuments(updatedValues, new DocumentReplaceOptions().returnNew(true)).get(); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + for (DocumentUpdateEntity doc : updateResult.getDocuments()) { + RawData d = doc.getNew(); + assertThat(d) + .isNotNull() + .isInstanceOf(RawJson.class); + + JsonNode jn = SerdeUtils.INSTANCE.parseJson(((RawJson) d).get()); + assertThat(jn.has("foo")).isTrue(); + assertThat(jn.get("foo").textValue()).isEqualTo("bar"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getInfo(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final CollectionEntity result = collection.getInfo().get(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getPropeties(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final CollectionPropertiesEntity result = collection.getProperties().get(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME); + assertThat(result.getCount()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void changeProperties(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final CollectionPropertiesEntity properties = collection.getProperties().get(); + assertThat(properties.getWaitForSync()).isNotNull(); + if (isAtLeastVersion(3, 7)) { + assertThat(properties.getSchema()).isNull(); + } + + String schemaRule = ("{ " + " \"properties\": {" + " \"number\": {" + " " + + " \"type\": \"number\"" + " }" + " }" + " }").replaceAll("\\s", ""); + String schemaMessage = "The document has problems!"; + + CollectionPropertiesOptions updatedOptions = + new CollectionPropertiesOptions().waitForSync(!properties.getWaitForSync()).schema(new CollectionSchema().setLevel(CollectionSchema.Level.NEW).setMessage(schemaMessage).setRule(schemaRule)); + + final CollectionPropertiesEntity changedProperties = collection.changeProperties(updatedOptions).get(); + assertThat(changedProperties.getWaitForSync()).isNotNull(); + assertThat(changedProperties.getWaitForSync()).isEqualTo(!properties.getWaitForSync()); + if (isAtLeastVersion(3, 7)) { + assertThat(changedProperties.getSchema()).isNotNull(); + assertThat(changedProperties.getSchema().getLevel()).isEqualTo(CollectionSchema.Level.NEW); + assertThat(changedProperties.getSchema().getMessage()).isEqualTo(schemaMessage); + assertThat(changedProperties.getSchema().getRule()).isEqualTo(schemaRule); + } + + // revert changes + CollectionPropertiesEntity revertedProperties = collection.changeProperties(new CollectionPropertiesOptions() + .waitForSync(properties.getWaitForSync()).schema(new CollectionSchema())).get(); + if (isAtLeastVersion(3, 7)) { + assertThat(revertedProperties.getSchema()).isNull(); + } + + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void rename(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + ArangoDatabaseAsync db = collection.db(); + + if (!db.collection("c1").exists().get()) { + db.collection("c1").create(); + } + + if (db.collection("c2").exists().get()) { + db.collection("c2").drop(); + } + + final CollectionEntity result = db.collection("c1").rename("c2").get(); + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo("c2"); + + final CollectionEntity info = db.collection("c2").getInfo().get(); + assertThat(info.getName()).isEqualTo("c2"); + + Throwable thrown = catchThrowable(() -> db.collection("c1").getInfo().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(404); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void responsibleShard(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + assumeTrue(isAtLeastVersion(3, 5)); + ShardEntity shard = collection.getResponsibleShard(new BaseDocument("testKey")).get(); + assertThat(shard).isNotNull(); + assertThat(shard.getShardId()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getRevision(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final CollectionRevisionEntity result = collection.getRevision().get(); + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME); + assertThat(result.getRevision()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void keyWithSpecialCharacter(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final String key = "myKey_-:.@()+,=;$!*'%-" + UUID.randomUUID(); + collection.insertDocument(new BaseDocument(key)).get(); + final BaseDocument doc = collection.getDocument(key, BaseDocument.class).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getKey()).isEqualTo(key); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void alreadyUrlEncodedkey(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + final String key = "http%3A%2F%2Fexample.com%2F-" + UUID.randomUUID(); + collection.insertDocument(new BaseDocument(key)).get(); + final BaseDocument doc = collection.getDocument(key, BaseDocument.class).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getKey()).isEqualTo(key); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void grantAccessRW(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + ArangoDBAsync arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null).get(); + collection.grantAccess("user1", Permissions.RW); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void grantAccessRO(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + ArangoDBAsync arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null).get(); + collection.grantAccess("user1", Permissions.RO).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void grantAccessNONE(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + ArangoDBAsync arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null).get(); + collection.grantAccess("user1", Permissions.NONE).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void grantAccessUserNotFound(ArangoCollectionAsync collection) { + Throwable thrown = catchThrowable(() -> collection.grantAccess("user1", Permissions.RW).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void revokeAccess(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + ArangoDBAsync arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null).get(); + collection.grantAccess("user1", Permissions.NONE).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void revokeAccessUserNotFound(ArangoCollectionAsync collection) { + Throwable thrown = catchThrowable(() -> collection.grantAccess("user1", Permissions.NONE).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void resetAccess(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + ArangoDBAsync arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null).get(); + collection.resetAccess("user1").get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void resetAccessUserNotFound(ArangoCollectionAsync collection) { + Throwable thrown = catchThrowable(() -> collection.resetAccess("user1").get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getPermissions(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assertThat(collection.getPermissions("root").get()).isEqualTo(Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void annotationsInParamsAndMethods(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(collection.getSerde().getUserSerde() instanceof JacksonSerde, "JacksonSerde only"); + AnnotatedEntity entity = new AnnotatedEntity(UUID.randomUUID().toString()); + AnnotatedEntity doc = collection.insertDocument(entity, new DocumentCreateOptions().returnNew(true)).get().getNew(); + assertThat(doc).isNotNull(); + assertThat(doc.getKey()).isEqualTo(entity.getKey()); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "type") + public interface Animal { + String getKey(); + + String getName(); + } + + public static class Dog implements Animal { + + @Key + private String key; + private String name; + + public Dog() { + } + + public Dog(String key, String name) { + this.key = key; + this.name = name; + } + + @Override + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class Cat implements Animal { + @Key + private String key; + private String name; + + public Cat() { + } + + public Cat(String key, String name) { + this.key = key; + this.name = name; + } + + @Override + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class TestUpdateEntity { + private String a, b; + + public String getA() { + return a; + } + + public String getB() { + return b; + } + } + + public static class TestUpdateEntitySerializeNullFalse { + private String a, b; + + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getA() { + return a; + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + public String getB() { + return b; + } + } + + public static class AnnotatedEntity { + + private final String key; + private String id; + private String rev; + + public AnnotatedEntity(@Key String key) { + this.key = key; + } + + public String getKey() { + return key; + } + + public String getId() { + return id; + } + + @Id + public void setId(String id) { + this.id = id; + } + + public String getRev() { + return rev; + } + + @Rev + public void setRev(String rev) { + this.rev = rev; + } + } + +} From 8bfaa1731c9f00c00b43d4dbfedfb2f83d0b6eda Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 13 Oct 2023 14:14:54 +0200 Subject: [PATCH 22/62] ArangoDBAsync.metrics() --- core/src/main/java/com/arangodb/ArangoDBAsync.java | 8 ++++---- .../java/com/arangodb/internal/ArangoDBAsyncImpl.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/arangodb/ArangoDBAsync.java b/core/src/main/java/com/arangodb/ArangoDBAsync.java index 6224eda84..9639a6dee 100644 --- a/core/src/main/java/com/arangodb/ArangoDBAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDBAsync.java @@ -61,10 +61,10 @@ public interface ArangoDBAsync extends ArangoSerdeAccessor { */ ArangoDatabaseAsync db(String name); -// /** -// * @return entry point for accessing client metrics -// */ -// ArangoMetrics metrics(); + /** + * @return entry point for accessing client metrics + */ + ArangoMetrics metrics(); /** * Asynchronous version of {@link ArangoDB#createDatabase(String)} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java index 14753cb0e..173a211fb 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java @@ -62,10 +62,10 @@ public ArangoDatabaseAsync db(final String dbName) { return new ArangoDatabaseAsyncImpl(this, dbName); } -// @Override -// public ArangoMetrics metrics() { -// return new ArangoMetricsImpl(executorAsync().getQueueTimeMetrics()); -// } + @Override + public ArangoMetrics metrics() { + return new ArangoMetricsImpl(executorAsync().getQueueTimeMetrics()); + } @Override public CompletableFuture createDatabase(final String dbName) { From bc2ea566b93cb33a0263bed5beebe22afeff285a Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 13 Oct 2023 15:16:42 +0200 Subject: [PATCH 23/62] ArangoGraphAsync --- .../java/com/arangodb/ArangoGraphAsync.java | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 core/src/main/java/com/arangodb/ArangoGraphAsync.java diff --git a/core/src/main/java/com/arangodb/ArangoGraphAsync.java b/core/src/main/java/com/arangodb/ArangoGraphAsync.java new file mode 100644 index 000000000..5752bf172 --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoGraphAsync.java @@ -0,0 +1,130 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.EdgeDefinition; +import com.arangodb.entity.GraphEntity; +import com.arangodb.model.GraphCreateOptions; +import com.arangodb.model.ReplaceEdgeDefinitionOptions; +import com.arangodb.model.VertexCollectionCreateOptions; + +import javax.annotation.concurrent.ThreadSafe; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +/** + * Asynchronous version of {@link ArangoGraph} + */ +@ThreadSafe +public interface ArangoGraphAsync extends ArangoSerdeAccessor { + + /** + * @return database async API + */ + ArangoDatabaseAsync db(); + + /** + * @return graph name + */ + String name(); + + /** + * Asynchronous version of {@link ArangoGraph#exists()} + */ + CompletableFuture exists(); + + /** + * Asynchronous version of {@link ArangoGraph#create(Iterable)} + */ + CompletableFuture create(Iterable edgeDefinitions); + + /** + * Asynchronous version of {@link ArangoGraph#create(Iterable, GraphCreateOptions)} + */ + CompletableFuture create(Iterable edgeDefinitions, GraphCreateOptions options); + + /** + * Asynchronous version of {@link ArangoGraph#drop()} + */ + CompletableFuture drop(); + + /** + * Asynchronous version of {@link ArangoGraph#drop(boolean)} + */ + CompletableFuture drop(boolean dropCollections); + + /** + * Asynchronous version of {@link ArangoGraph#getInfo()} + */ + CompletableFuture getInfo(); + + /** + * Asynchronous version of {@link ArangoGraph#getVertexCollections()} + */ + CompletableFuture> getVertexCollections(); + + /** + * Asynchronous version of {@link ArangoGraph#addVertexCollection(String)} + */ + CompletableFuture addVertexCollection(String name); + + /** + * Asynchronous version of {@link ArangoGraph#addVertexCollection(String, VertexCollectionCreateOptions)} + */ + CompletableFuture addVertexCollection(String name, VertexCollectionCreateOptions options); + +// /** +// * Returns a {@code ArangoVertexCollectionAsync} instance for the given vertex collection name. +// * +// * @param name Name of the vertex collection +// * @return collection handler +// */ +// ArangoVertexCollectionAsync vertexCollection(String name); +// +// /** +// * Returns a {@code ArangoEdgeCollectionAsync} instance for the given edge collection name. +// * +// * @param name Name of the edge collection +// * @return collection handler +// */ +// ArangoEdgeCollectionAsync edgeCollection(String name); + + /** + * Asynchronous version of {@link ArangoGraph#getEdgeDefinitions()} + */ + CompletableFuture> getEdgeDefinitions(); + + /** + * Asynchronous version of {@link ArangoGraph#addEdgeDefinition(EdgeDefinition)} + */ + CompletableFuture addEdgeDefinition(EdgeDefinition definition); + + /** + * Asynchronous version of {@link ArangoGraph#replaceEdgeDefinition(EdgeDefinition)} + */ + CompletableFuture replaceEdgeDefinition(EdgeDefinition definition); + + /** + * Asynchronous version of {@link ArangoGraph#replaceEdgeDefinition(EdgeDefinition, ReplaceEdgeDefinitionOptions)} + */ + CompletableFuture replaceEdgeDefinition(EdgeDefinition definition, ReplaceEdgeDefinitionOptions options); + +} From ecf9c7dd4eea82fffde7193486eee730b751f062 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 16 Oct 2023 10:12:09 +0200 Subject: [PATCH 24/62] ArangoGraphAsyncImpl --- .../com/arangodb/ArangoDatabaseAsync.java | 14 +- .../internal/ArangoCollectionAsyncImpl.java | 8 +- .../internal/ArangoDatabaseAsyncImpl.java | 13 +- .../internal/ArangoGraphAsyncImpl.java | 134 ++++++++++++++++++ .../arangodb/internal/ArangoGraphImpl.java | 15 +- .../internal/InternalArangoGraph.java | 29 ++-- 6 files changed, 171 insertions(+), 42 deletions(-) create mode 100644 core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java diff --git a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java index b4a10262b..09c049427 100644 --- a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java @@ -228,13 +228,13 @@ public interface ArangoDatabaseAsync extends ArangoSerdeAccessor { */ CompletableFuture> getAqlFunctions(AqlFunctionGetOptions options); -// /** -// * Returns a {@code ArangoGraph} instance for the given graph name. -// * -// * @param name Name of the graph -// * @return graph handler -// */ -// ArangoGraph graph(String name); + /** + * Returns a {@code ArangoGraphAsync} instance for the given graph name. + * + * @param name Name of the graph + * @return graph handler + */ + ArangoGraphAsync graph(String name); /** * Asynchronous version of {@link ArangoDatabase#createGraph(String, Iterable)} diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java index 445bb9168..06b4944ab 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java @@ -20,13 +20,13 @@ package com.arangodb.internal; -import com.arangodb.*; +import com.arangodb.ArangoCollectionAsync; +import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabaseAsync; import com.arangodb.entity.*; import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; import com.arangodb.util.RawData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Objects; @@ -41,8 +41,6 @@ */ public class ArangoCollectionAsyncImpl extends InternalArangoCollection implements ArangoCollectionAsync { - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoCollectionAsyncImpl.class); - private final ArangoDatabaseAsync db; protected ArangoCollectionAsyncImpl(final ArangoDatabaseAsyncImpl db, final String name) { diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index 5702b7232..440ca1e61 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -20,10 +20,7 @@ package com.arangodb.internal; -import com.arangodb.ArangoCollectionAsync; -import com.arangodb.ArangoDBAsync; -import com.arangodb.ArangoDBException; -import com.arangodb.ArangoDatabaseAsync; +import com.arangodb.*; import com.arangodb.entity.*; import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; import com.arangodb.internal.util.DocumentUtil; @@ -311,10 +308,10 @@ public CompletableFuture> getAqlFunctions(final Aq return executorAsync().execute(getAqlFunctionsRequest(options), getAqlFunctionsResponseDeserializer()); } -// @Override -// public ArangoGraph graph(final String name) { -// return new ArangoGraphImpl(this, name); -// } + @Override + public ArangoGraphAsync graph(final String name) { + return new ArangoGraphAsyncImpl(this, name); + } @Override public CompletableFuture createGraph(final String name, final Iterable edgeDefinitions) { diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java new file mode 100644 index 000000000..c126aba34 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java @@ -0,0 +1,134 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.*; +import com.arangodb.entity.EdgeDefinition; +import com.arangodb.entity.GraphEntity; +import com.arangodb.model.GraphCreateOptions; +import com.arangodb.model.ReplaceEdgeDefinitionOptions; +import com.arangodb.model.VertexCollectionCreateOptions; + +import java.util.Collection; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +public class ArangoGraphAsyncImpl extends InternalArangoGraph implements ArangoGraphAsync { + + private final ArangoDatabaseAsync db; + + protected ArangoGraphAsyncImpl(final ArangoDatabaseAsyncImpl db, final String name) { + super(db, db.name(), name); + this.db = db; + } + + @Override + public ArangoDatabaseAsync db() { + return db; + } + + @Override + public CompletableFuture exists() { + return getInfo() + .thenApply(Objects::nonNull) + .exceptionally(e -> { + if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e.getCause(); + if (ArangoErrors.ERROR_GRAPH_NOT_FOUND.equals(aEx.getErrorNum())) { + return false; + } + } + throw new CompletionException(e); + }); + } + + @Override + public CompletableFuture create(final Iterable edgeDefinitions) { + return db().createGraph(name(), edgeDefinitions); + } + + @Override + public CompletableFuture create(final Iterable edgeDefinitions, final GraphCreateOptions options) { + return db().createGraph(name(), edgeDefinitions, options); + } + + @Override + public CompletableFuture drop() { + return executorAsync().execute(dropRequest(), Void.class); + } + + @Override + public CompletableFuture drop(final boolean dropCollections) { + return executorAsync().execute(dropRequest(dropCollections), Void.class); + } + + @Override + public CompletableFuture getInfo() { + return executorAsync().execute(getInfoRequest(), getInfoResponseDeserializer()); + } + + @Override + public CompletableFuture> getVertexCollections() { + return executorAsync().execute(getVertexCollectionsRequest(), getVertexCollectionsResponseDeserializer()); + } + + @Override + public CompletableFuture addVertexCollection(final String name) { + return addVertexCollection(name, new VertexCollectionCreateOptions()); + } + + @Override + public CompletableFuture addVertexCollection(final String name, final VertexCollectionCreateOptions options) { + return executorAsync().execute(addVertexCollectionRequest(name, options), addVertexCollectionResponseDeserializer()); + } + +// @Override +// public ArangoVertexCollection vertexCollection(final String name) { +// return new ArangoVertexCollectionImpl(this, name); +// } +// +// @Override +// public ArangoEdgeCollection edgeCollection(final String name) { +// return new ArangoEdgeCollectionImpl(this, name); +// } + + @Override + public CompletableFuture> getEdgeDefinitions() { + return executorAsync().execute(getEdgeDefinitionsRequest(), getEdgeDefinitionsDeserializer()); + } + + @Override + public CompletableFuture addEdgeDefinition(final EdgeDefinition definition) { + return executorAsync().execute(addEdgeDefinitionRequest(definition), addEdgeDefinitionResponseDeserializer()); + } + + @Override + public CompletableFuture replaceEdgeDefinition(final EdgeDefinition definition) { + return replaceEdgeDefinition(definition, new ReplaceEdgeDefinitionOptions()); + } + + @Override + public CompletableFuture replaceEdgeDefinition(final EdgeDefinition definition, final ReplaceEdgeDefinitionOptions options) { + return executorAsync().execute(replaceEdgeDefinitionRequest(definition, options), replaceEdgeDefinitionResponseDeserializer()); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java index 6e63fcfe5..53373f585 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphImpl.java @@ -20,10 +20,7 @@ package com.arangodb.internal; -import com.arangodb.ArangoDBException; -import com.arangodb.ArangoEdgeCollection; -import com.arangodb.ArangoGraph; -import com.arangodb.ArangoVertexCollection; +import com.arangodb.*; import com.arangodb.entity.EdgeDefinition; import com.arangodb.entity.GraphEntity; import com.arangodb.model.GraphCreateOptions; @@ -37,8 +34,16 @@ */ public class ArangoGraphImpl extends InternalArangoGraph implements ArangoGraph { + private final ArangoDatabase db; + protected ArangoGraphImpl(final ArangoDatabaseImpl db, final String name) { - super(db, name); + super(db, db.name(), name); + this.db = db; + } + + @Override + public ArangoDatabase db() { + return db; } @Override diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java b/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java index 1b941cb21..85d3e90ad 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoGraph.java @@ -20,7 +20,6 @@ package com.arangodb.internal; -import com.arangodb.ArangoDatabase; import com.arangodb.entity.EdgeDefinition; import com.arangodb.entity.GraphEntity; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; @@ -42,19 +41,15 @@ public abstract class InternalArangoGraph extends ArangoExecuteable { private static final String VERTEX = "vertex"; private static final String EDGE = "edge"; - private final ArangoDatabaseImpl db; - private final String name; + protected final String dbName; + protected final String name; - protected InternalArangoGraph(final ArangoDatabaseImpl db, final String name) { - super(db); - this.db = db; + protected InternalArangoGraph(final ArangoExecuteable executeable, final String dbName, final String name) { + super(executeable); + this.dbName = dbName; this.name = name; } - public ArangoDatabase db() { - return db; - } - public String name() { return name; } @@ -64,7 +59,7 @@ protected InternalRequest dropRequest() { } protected InternalRequest dropRequest(final boolean dropCollections) { - final InternalRequest request = request(db.name(), RequestType.DELETE, PATH_API_GHARIAL, name); + final InternalRequest request = request(dbName, RequestType.DELETE, PATH_API_GHARIAL, name); if (dropCollections) { request.putQueryParam("dropCollections", true); } @@ -72,7 +67,7 @@ protected InternalRequest dropRequest(final boolean dropCollections) { } protected InternalRequest getInfoRequest() { - return request(db.name(), RequestType.GET, PATH_API_GHARIAL, name); + return request(dbName, RequestType.GET, PATH_API_GHARIAL, name); } protected ResponseDeserializer getInfoResponseDeserializer() { @@ -80,7 +75,7 @@ protected ResponseDeserializer getInfoResponseDeserializer() { } protected InternalRequest getVertexCollectionsRequest() { - return request(db.name(), RequestType.GET, PATH_API_GHARIAL, name, VERTEX); + return request(dbName, RequestType.GET, PATH_API_GHARIAL, name, VERTEX); } protected ResponseDeserializer> getVertexCollectionsResponseDeserializer() { @@ -89,7 +84,7 @@ protected ResponseDeserializer> getVertexCollectionsResponseD } protected InternalRequest addVertexCollectionRequest(final String name, final VertexCollectionCreateOptions options) { - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_GHARIAL, name(), VERTEX); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_GHARIAL, name(), VERTEX); request.setBody(getSerde().serialize(OptionsBuilder.build(options, name))); return request; } @@ -99,7 +94,7 @@ protected ResponseDeserializer addVertexCollectionResponseDeseriali } protected InternalRequest getEdgeDefinitionsRequest() { - return request(db.name(), RequestType.GET, PATH_API_GHARIAL, name, EDGE); + return request(dbName, RequestType.GET, PATH_API_GHARIAL, name, EDGE); } protected ResponseDeserializer> getEdgeDefinitionsDeserializer() { @@ -108,7 +103,7 @@ protected ResponseDeserializer> getEdgeDefinitionsDeserialize } protected InternalRequest addEdgeDefinitionRequest(final EdgeDefinition definition) { - final InternalRequest request = request(db.name(), RequestType.POST, PATH_API_GHARIAL, name, EDGE); + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_GHARIAL, name, EDGE); request.setBody(getSerde().serialize(definition)); return request; } @@ -119,7 +114,7 @@ protected ResponseDeserializer addEdgeDefinitionResponseDeserialize protected InternalRequest replaceEdgeDefinitionRequest(final EdgeDefinition definition, final ReplaceEdgeDefinitionOptions options) { final InternalRequest request = - request(db.name(), RequestType.PUT, PATH_API_GHARIAL, name, EDGE, definition.getCollection()) + request(dbName, RequestType.PUT, PATH_API_GHARIAL, name, EDGE, definition.getCollection()) .putQueryParam("waitForSync", options.getWaitForSync()) .putQueryParam("dropCollections", options.getDropCollections()); request.setBody(getSerde().serialize(definition)); From 91728cf49a2fbafc5d82b618f6004061b78df217 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 16 Oct 2023 10:41:45 +0200 Subject: [PATCH 25/62] ArangoVertexCollectionAsync --- .../arangodb/ArangoVertexCollectionAsync.java | 110 +++++++++++++++ .../ArangoVertexCollectionAsyncImpl.java | 131 ++++++++++++++++++ .../internal/ArangoVertexCollectionImpl.java | 10 +- .../InternalArangoVertexCollection.java | 30 ++-- 4 files changed, 265 insertions(+), 16 deletions(-) create mode 100644 core/src/main/java/com/arangodb/ArangoVertexCollectionAsync.java create mode 100644 core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java diff --git a/core/src/main/java/com/arangodb/ArangoVertexCollectionAsync.java b/core/src/main/java/com/arangodb/ArangoVertexCollectionAsync.java new file mode 100644 index 000000000..a20f42247 --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoVertexCollectionAsync.java @@ -0,0 +1,110 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.VertexEntity; +import com.arangodb.entity.VertexUpdateEntity; +import com.arangodb.model.*; + +import javax.annotation.concurrent.ThreadSafe; +import java.util.concurrent.CompletableFuture; + +/** + * Asynchronous version of {@link ArangoVertexCollection} + */ +@ThreadSafe +public interface ArangoVertexCollectionAsync extends ArangoSerdeAccessor { + + /** + * The the handler of the named graph the edge collection is within + * + * @return graph handler + */ + ArangoGraphAsync graph(); + + /** + * The name of the edge collection + * + * @return collection name + */ + String name(); + + /** + * Asynchronous version of {@link ArangoVertexCollection#drop()} + */ + CompletableFuture drop(); + + /** + * Asynchronous version of {@link ArangoVertexCollection#drop(VertexCollectionDropOptions)} + */ + CompletableFuture drop(VertexCollectionDropOptions options); + + /** + * Asynchronous version of {@link ArangoVertexCollection#insertVertex(Object)} + */ + CompletableFuture insertVertex(Object value); + + /** + * Asynchronous version of {@link ArangoVertexCollection#insertVertex(Object, VertexCreateOptions)} + */ + CompletableFuture insertVertex(Object value, VertexCreateOptions options); + + /** + * Asynchronous version of {@link ArangoVertexCollection#getVertex(String, Class)} + */ + CompletableFuture getVertex(String key, Class type); + + /** + * Asynchronous version of {@link ArangoVertexCollection#getVertex(String, Class, GraphDocumentReadOptions)} + */ + CompletableFuture getVertex(String key, Class type, GraphDocumentReadOptions options); + + /** + * Asynchronous version of {@link ArangoVertexCollection#replaceVertex(String, Object)} + */ + CompletableFuture replaceVertex(String key, Object value); + + /** + * Asynchronous version of {@link ArangoVertexCollection#replaceVertex(String, Object, VertexReplaceOptions)} + */ + CompletableFuture replaceVertex(String key, Object value, VertexReplaceOptions options); + + /** + * Asynchronous version of {@link ArangoVertexCollection#updateVertex(String, Object)} + */ + CompletableFuture updateVertex(String key, Object value); + + /** + * Asynchronous version of {@link ArangoVertexCollection#updateVertex(String, Object, VertexUpdateOptions)} + */ + CompletableFuture updateVertex(String key, Object value, VertexUpdateOptions options); + + /** + * Asynchronous version of {@link ArangoVertexCollection#deleteVertex(String)} + */ + CompletableFuture deleteVertex(String key); + + /** + * Asynchronous version of {@link ArangoVertexCollection#deleteVertex(String, VertexDeleteOptions)} + */ + CompletableFuture deleteVertex(String key, VertexDeleteOptions options); + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java new file mode 100644 index 000000000..72f960a7a --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java @@ -0,0 +1,131 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.ArangoDBException; +import com.arangodb.ArangoGraphAsync; +import com.arangodb.ArangoVertexCollectionAsync; +import com.arangodb.entity.VertexEntity; +import com.arangodb.entity.VertexUpdateEntity; +import com.arangodb.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; + +/** + * @author Mark Vollmary + */ +public class ArangoVertexCollectionAsyncImpl extends InternalArangoVertexCollection implements ArangoVertexCollectionAsync { + + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoVertexCollectionAsyncImpl.class); + + private final ArangoGraphAsync graph; + + protected ArangoVertexCollectionAsyncImpl(final ArangoGraphAsyncImpl graph, final String name) { + super(graph, graph.db().name(), graph.name(), name); + this.graph = graph; + } + + @Override + public ArangoGraphAsync graph() { + return graph; + } + + @Override + public CompletableFuture drop() { + return drop(new VertexCollectionDropOptions()); + } + + @Override + public CompletableFuture drop(final VertexCollectionDropOptions options) { + return executorAsync().execute(dropRequest(options), Void.class); + } + + @Override + public CompletableFuture insertVertex(final Object value) { + return executorAsync().execute(insertVertexRequest(value, new VertexCreateOptions()), + insertVertexResponseDeserializer()); + } + + @Override + public CompletableFuture insertVertex(final Object value, final VertexCreateOptions options) { + return executorAsync().execute(insertVertexRequest(value, options), insertVertexResponseDeserializer()); + } + + @Override + public CompletableFuture getVertex(final String key, final Class type) { + try { + return executorAsync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), + getVertexResponseDeserializer(type)); + } catch (final ArangoDBException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + } + } + + @Override + public CompletableFuture getVertex(final String key, final Class type, final GraphDocumentReadOptions options) { + try { + return executorAsync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); + } catch (final ArangoDBException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + } + } + + @Override + public CompletableFuture replaceVertex(final String key, final Object value) { + return executorAsync().execute(replaceVertexRequest(key, value, new VertexReplaceOptions()), + replaceVertexResponseDeserializer()); + } + + @Override + public CompletableFuture replaceVertex(final String key, final Object value, final VertexReplaceOptions options) { + return executorAsync().execute(replaceVertexRequest(key, value, options), replaceVertexResponseDeserializer()); + } + + @Override + public CompletableFuture updateVertex(final String key, final Object value) { + return executorAsync().execute(updateVertexRequest(key, value, new VertexUpdateOptions()), + updateVertexResponseDeserializer()); + } + + @Override + public CompletableFuture updateVertex(final String key, final Object value, final VertexUpdateOptions options) { + return executorAsync().execute(updateVertexRequest(key, value, options), updateVertexResponseDeserializer()); + } + + @Override + public CompletableFuture deleteVertex(final String key) { + return executorAsync().execute(deleteVertexRequest(key, new VertexDeleteOptions()), Void.class); + } + + @Override + public CompletableFuture deleteVertex(final String key, final VertexDeleteOptions options) { + return executorAsync().execute(deleteVertexRequest(key, options), Void.class); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java index 2a4cdddf8..a41128ce0 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java @@ -21,6 +21,7 @@ package com.arangodb.internal; import com.arangodb.ArangoDBException; +import com.arangodb.ArangoGraph; import com.arangodb.ArangoVertexCollection; import com.arangodb.entity.VertexEntity; import com.arangodb.entity.VertexUpdateEntity; @@ -34,9 +35,16 @@ public class ArangoVertexCollectionImpl extends InternalArangoVertexCollection implements ArangoVertexCollection { private static final Logger LOGGER = LoggerFactory.getLogger(ArangoVertexCollectionImpl.class); + private final ArangoGraph graph; protected ArangoVertexCollectionImpl(final ArangoGraphImpl graph, final String name) { - super(graph, name); + super(graph, graph.db().name(), graph.name(), name); + this.graph = graph; + } + + @Override + public ArangoGraph graph() { + return graph; } @Override diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java index db25e9046..412622fad 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoVertexCollection.java @@ -20,7 +20,6 @@ package com.arangodb.internal; -import com.arangodb.ArangoGraph; import com.arangodb.entity.VertexEntity; import com.arangodb.entity.VertexUpdateEntity; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; @@ -38,30 +37,31 @@ public abstract class InternalArangoVertexCollection extends ArangoExecuteable { private static final String VERTEX_JSON_POINTER = "/vertex"; private static final String TRANSACTION_ID = "x-arango-trx-id"; - private final ArangoGraphImpl graph; + private final String dbName; + private final String graphName; private final String name; - protected InternalArangoVertexCollection(final ArangoGraphImpl graph, final String name) { - super(graph); - this.graph = graph; + protected InternalArangoVertexCollection(final ArangoExecuteable executeable, + final String dbName, + final String graphName, + final String name) { + super(executeable); + this.dbName = dbName; + this.graphName = graphName; this.name = name; } - public ArangoGraph graph() { - return graph; - } - public String name() { return name; } protected InternalRequest dropRequest(final VertexCollectionDropOptions options) { - return request(graph.db().name(), RequestType.DELETE, PATH_API_GHARIAL, graph.name(), VERTEX_PATH, name) + return request(dbName, RequestType.DELETE, PATH_API_GHARIAL, graphName, VERTEX_PATH, name) .putQueryParam("dropCollection", options.getDropCollection()); } protected InternalRequest insertVertexRequest(final T value, final VertexCreateOptions options) { - final InternalRequest request = request(graph.db().name(), RequestType.POST, PATH_API_GHARIAL, graph.name(), VERTEX_PATH, + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_GHARIAL, graphName, VERTEX_PATH, name); final VertexCreateOptions params = (options != null ? options : new VertexCreateOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -75,7 +75,7 @@ protected ResponseDeserializer insertVertexResponseDeserializer() } protected InternalRequest getVertexRequest(final String key, final GraphDocumentReadOptions options) { - final InternalRequest request = request(graph.db().name(), RequestType.GET, PATH_API_GHARIAL, graph.name(), VERTEX_PATH, + final InternalRequest request = request(dbName, RequestType.GET, PATH_API_GHARIAL, graphName, VERTEX_PATH, DocumentUtil.createDocumentHandle(name, key)); final GraphDocumentReadOptions params = (options != null ? options : new GraphDocumentReadOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -92,7 +92,7 @@ protected ResponseDeserializer getVertexResponseDeserializer(final Class< } protected InternalRequest replaceVertexRequest(final String key, final T value, final VertexReplaceOptions options) { - final InternalRequest request = request(graph.db().name(), RequestType.PUT, PATH_API_GHARIAL, graph.name(), VERTEX_PATH, + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_GHARIAL, graphName, VERTEX_PATH, DocumentUtil.createDocumentHandle(name, key)); final VertexReplaceOptions params = (options != null ? options : new VertexReplaceOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -108,7 +108,7 @@ protected ResponseDeserializer replaceVertexResponseDeserial protected InternalRequest updateVertexRequest(final String key, final T value, final VertexUpdateOptions options) { final InternalRequest request; - request = request(graph.db().name(), RequestType.PATCH, PATH_API_GHARIAL, graph.name(), VERTEX_PATH, + request = request(dbName, RequestType.PATCH, PATH_API_GHARIAL, graphName, VERTEX_PATH, DocumentUtil.createDocumentHandle(name, key)); final VertexUpdateOptions params = (options != null ? options : new VertexUpdateOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -124,7 +124,7 @@ protected ResponseDeserializer updateVertexResponseDeseriali } protected InternalRequest deleteVertexRequest(final String key, final VertexDeleteOptions options) { - final InternalRequest request = request(graph.db().name(), RequestType.DELETE, PATH_API_GHARIAL, graph.name(), + final InternalRequest request = request(dbName, RequestType.DELETE, PATH_API_GHARIAL, graphName, VERTEX_PATH, DocumentUtil.createDocumentHandle(name, key)); final VertexDeleteOptions params = (options != null ? options : new VertexDeleteOptions()); From 2d88961ab21a590237973bd050f860bbab38b29b Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 16 Oct 2023 11:12:54 +0200 Subject: [PATCH 26/62] ArangoEdgeCollectionAsync --- .../arangodb/ArangoEdgeCollectionAsync.java | 110 +++++++++++++++++ .../java/com/arangodb/ArangoGraphAsync.java | 16 +-- .../ArangoEdgeCollectionAsyncImpl.java | 114 ++++++++++++++++++ .../internal/ArangoEdgeCollectionImpl.java | 34 ++---- .../internal/ArangoGraphAsyncImpl.java | 10 +- .../ArangoVertexCollectionAsyncImpl.java | 25 +--- .../internal/ArangoVertexCollectionImpl.java | 24 +--- .../InternalArangoEdgeCollection.java | 30 ++--- 8 files changed, 270 insertions(+), 93 deletions(-) create mode 100644 core/src/main/java/com/arangodb/ArangoEdgeCollectionAsync.java create mode 100644 core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java diff --git a/core/src/main/java/com/arangodb/ArangoEdgeCollectionAsync.java b/core/src/main/java/com/arangodb/ArangoEdgeCollectionAsync.java new file mode 100644 index 000000000..bfffe9aab --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoEdgeCollectionAsync.java @@ -0,0 +1,110 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.EdgeEntity; +import com.arangodb.entity.EdgeUpdateEntity; +import com.arangodb.model.*; + +import javax.annotation.concurrent.ThreadSafe; +import java.util.concurrent.CompletableFuture; + +/** + * Asynchronous version of {@link ArangoEdgeCollection} + */ +@ThreadSafe +public interface ArangoEdgeCollectionAsync extends ArangoSerdeAccessor { + + /** + * The the handler of the named graph the edge collection is within + * + * @return graph handler + */ + ArangoGraphAsync graph(); + + /** + * The name of the edge collection + * + * @return collection name + */ + String name(); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#drop()} + */ + CompletableFuture drop(); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#drop(EdgeCollectionDropOptions)} + */ + CompletableFuture drop(EdgeCollectionDropOptions options); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#insertEdge(Object)} + */ + CompletableFuture insertEdge(Object value); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#insertEdge(Object, EdgeCreateOptions)} + */ + CompletableFuture insertEdge(Object value, EdgeCreateOptions options); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#getEdge(String, Class)} + */ + CompletableFuture getEdge(String key, Class type); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#getEdge(String, Class, GraphDocumentReadOptions)} + */ + CompletableFuture getEdge(String key, Class type, GraphDocumentReadOptions options); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#replaceEdge(String, Object)} + */ + CompletableFuture replaceEdge(String key, Object value); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#replaceEdge(String, Object, EdgeReplaceOptions)} + */ + CompletableFuture replaceEdge(String key, Object value, EdgeReplaceOptions options); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#updateEdge(String, Object)} + */ + CompletableFuture updateEdge(String key, Object value); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#updateEdge(String, Object, EdgeUpdateOptions)} + */ + CompletableFuture updateEdge(String key, Object value, EdgeUpdateOptions options); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#deleteEdge(String)} + */ + CompletableFuture deleteEdge(String key); + + /** + * Asynchronous version of {@link ArangoEdgeCollection#deleteEdge(String, EdgeDeleteOptions)} + */ + CompletableFuture deleteEdge(String key, EdgeDeleteOptions options); + +} diff --git a/core/src/main/java/com/arangodb/ArangoGraphAsync.java b/core/src/main/java/com/arangodb/ArangoGraphAsync.java index 5752bf172..9b0974643 100644 --- a/core/src/main/java/com/arangodb/ArangoGraphAsync.java +++ b/core/src/main/java/com/arangodb/ArangoGraphAsync.java @@ -91,14 +91,14 @@ public interface ArangoGraphAsync extends ArangoSerdeAccessor { */ CompletableFuture addVertexCollection(String name, VertexCollectionCreateOptions options); -// /** -// * Returns a {@code ArangoVertexCollectionAsync} instance for the given vertex collection name. -// * -// * @param name Name of the vertex collection -// * @return collection handler -// */ -// ArangoVertexCollectionAsync vertexCollection(String name); -// + /** + * Returns a {@code ArangoVertexCollectionAsync} instance for the given vertex collection name. + * + * @param name Name of the vertex collection + * @return collection handler + */ + ArangoVertexCollectionAsync vertexCollection(String name); + // /** // * Returns a {@code ArangoEdgeCollectionAsync} instance for the given edge collection name. // * diff --git a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java new file mode 100644 index 000000000..f24795f83 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java @@ -0,0 +1,114 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.*; +import com.arangodb.entity.EdgeEntity; +import com.arangodb.entity.EdgeUpdateEntity; +import com.arangodb.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; + +/** + * @author Mark Vollmary + */ +public class ArangoEdgeCollectionAsyncImpl extends InternalArangoEdgeCollection implements ArangoEdgeCollectionAsync { + + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoEdgeCollectionAsyncImpl.class); + private final ArangoGraphAsync graph; + + protected ArangoEdgeCollectionAsyncImpl(final ArangoGraphAsyncImpl graph, final String name) { + super(graph, graph.db().name(), graph.name(), name); + this.graph = graph; + } + + @Override + public ArangoGraphAsync graph() { + return graph; + } + + @Override + public CompletableFuture drop() { + return drop(new EdgeCollectionDropOptions()); + } + + @Override + public CompletableFuture drop(final EdgeCollectionDropOptions options) { + return executorAsync().execute(removeEdgeDefinitionRequest(options), Void.class); + } + + @Override + public CompletableFuture insertEdge(final Object value) { + return executorAsync().execute(insertEdgeRequest(value, new EdgeCreateOptions()), + insertEdgeResponseDeserializer()); + } + + @Override + public CompletableFuture insertEdge(final Object value, final EdgeCreateOptions options) { + return executorAsync().execute(insertEdgeRequest(value, options), insertEdgeResponseDeserializer()); + } + + @Override + public CompletableFuture getEdge(final String key, final Class type) { + return executorAsync().execute(getEdgeRequest(key, new GraphDocumentReadOptions()), + getEdgeResponseDeserializer(type)); + } + + @Override + public CompletableFuture getEdge(final String key, final Class type, final GraphDocumentReadOptions options) { + return executorAsync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)); + } + + @Override + public CompletableFuture replaceEdge(final String key, final Object value) { + return executorAsync().execute(replaceEdgeRequest(key, value, new EdgeReplaceOptions()), + replaceEdgeResponseDeserializer()); + } + + @Override + public CompletableFuture replaceEdge(final String key, final Object value, final EdgeReplaceOptions options) { + return executorAsync().execute(replaceEdgeRequest(key, value, options), replaceEdgeResponseDeserializer()); + } + + @Override + public CompletableFuture updateEdge(final String key, final Object value) { + return executorAsync().execute(updateEdgeRequest(key, value, new EdgeUpdateOptions()), + updateEdgeResponseDeserializer()); + } + + @Override + public CompletableFuture updateEdge(final String key, final Object value, final EdgeUpdateOptions options) { + return executorAsync().execute(updateEdgeRequest(key, value, options), updateEdgeResponseDeserializer()); + } + + @Override + public CompletableFuture deleteEdge(final String key) { + return executorAsync().execute(deleteEdgeRequest(key, new EdgeDeleteOptions()), Void.class); + } + + @Override + public CompletableFuture deleteEdge(final String key, final EdgeDeleteOptions options) { + return executorAsync().execute(deleteEdgeRequest(key, options), Void.class); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java index a0ed64185..d2bfebff1 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java @@ -20,23 +20,27 @@ package com.arangodb.internal; -import com.arangodb.ArangoDBException; import com.arangodb.ArangoEdgeCollection; +import com.arangodb.ArangoGraph; import com.arangodb.entity.EdgeEntity; import com.arangodb.entity.EdgeUpdateEntity; import com.arangodb.model.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * @author Mark Vollmary */ public class ArangoEdgeCollectionImpl extends InternalArangoEdgeCollection implements ArangoEdgeCollection { - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoEdgeCollectionImpl.class); + private final ArangoGraphImpl graph; protected ArangoEdgeCollectionImpl(final ArangoGraphImpl graph, final String name) { - super(graph, name); + super(graph, graph.db().name(), graph.name(), name); + this.graph = graph; + } + + @Override + public ArangoGraph graph() { + return graph; } @Override @@ -62,27 +66,13 @@ public EdgeEntity insertEdge(final Object value, final EdgeCreateOptions options @Override public T getEdge(final String key, final Class type) { - try { - return executorSync().execute(getEdgeRequest(key, new GraphDocumentReadOptions()), - getEdgeResponseDeserializer(type)); - } catch (final ArangoDBException e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(e.getMessage(), e); - } - return null; - } + return executorSync().execute(getEdgeRequest(key, new GraphDocumentReadOptions()), + getEdgeResponseDeserializer(type)); } @Override public T getEdge(final String key, final Class type, final GraphDocumentReadOptions options) { - try { - return executorSync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)); - } catch (final ArangoDBException e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(e.getMessage(), e); - } - return null; - } + return executorSync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)); } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java index c126aba34..d4ae5c44d 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java @@ -101,11 +101,11 @@ public CompletableFuture addVertexCollection(final String name, fin return executorAsync().execute(addVertexCollectionRequest(name, options), addVertexCollectionResponseDeserializer()); } -// @Override -// public ArangoVertexCollection vertexCollection(final String name) { -// return new ArangoVertexCollectionImpl(this, name); -// } -// + @Override + public ArangoVertexCollectionAsync vertexCollection(final String name) { + return new ArangoVertexCollectionAsyncImpl(this, name); + } + // @Override // public ArangoEdgeCollection edgeCollection(final String name) { // return new ArangoEdgeCollectionImpl(this, name); diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java index 72f960a7a..4fa4f3f18 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java @@ -20,14 +20,11 @@ package com.arangodb.internal; -import com.arangodb.ArangoDBException; import com.arangodb.ArangoGraphAsync; import com.arangodb.ArangoVertexCollectionAsync; import com.arangodb.entity.VertexEntity; import com.arangodb.entity.VertexUpdateEntity; import com.arangodb.model.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.concurrent.CompletableFuture; @@ -36,8 +33,6 @@ */ public class ArangoVertexCollectionAsyncImpl extends InternalArangoVertexCollection implements ArangoVertexCollectionAsync { - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoVertexCollectionAsyncImpl.class); - private final ArangoGraphAsync graph; protected ArangoVertexCollectionAsyncImpl(final ArangoGraphAsyncImpl graph, final String name) { @@ -73,27 +68,13 @@ public CompletableFuture insertVertex(final Object value, final Ve @Override public CompletableFuture getVertex(final String key, final Class type) { - try { - return executorAsync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), - getVertexResponseDeserializer(type)); - } catch (final ArangoDBException e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(e.getMessage(), e); - } - return null; - } + return executorAsync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), + getVertexResponseDeserializer(type)); } @Override public CompletableFuture getVertex(final String key, final Class type, final GraphDocumentReadOptions options) { - try { - return executorAsync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); - } catch (final ArangoDBException e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(e.getMessage(), e); - } - return null; - } + return executorAsync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java index a41128ce0..6bdebf900 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java @@ -20,21 +20,17 @@ package com.arangodb.internal; -import com.arangodb.ArangoDBException; import com.arangodb.ArangoGraph; import com.arangodb.ArangoVertexCollection; import com.arangodb.entity.VertexEntity; import com.arangodb.entity.VertexUpdateEntity; import com.arangodb.model.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * @author Mark Vollmary */ public class ArangoVertexCollectionImpl extends InternalArangoVertexCollection implements ArangoVertexCollection { - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoVertexCollectionImpl.class); private final ArangoGraph graph; protected ArangoVertexCollectionImpl(final ArangoGraphImpl graph, final String name) { @@ -70,27 +66,13 @@ public VertexEntity insertVertex(final Object value, final VertexCreateOptions o @Override public T getVertex(final String key, final Class type) { - try { - return executorSync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), - getVertexResponseDeserializer(type)); - } catch (final ArangoDBException e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(e.getMessage(), e); - } - return null; - } + return executorSync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), + getVertexResponseDeserializer(type)); } @Override public T getVertex(final String key, final Class type, final GraphDocumentReadOptions options) { - try { - return executorSync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); - } catch (final ArangoDBException e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(e.getMessage(), e); - } - return null; - } + return executorSync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); } @Override diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoEdgeCollection.java b/core/src/main/java/com/arangodb/internal/InternalArangoEdgeCollection.java index 9c65f23d5..3466b0d92 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoEdgeCollection.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoEdgeCollection.java @@ -20,7 +20,6 @@ package com.arangodb.internal; -import com.arangodb.ArangoGraph; import com.arangodb.entity.EdgeEntity; import com.arangodb.entity.EdgeUpdateEntity; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; @@ -38,31 +37,32 @@ public abstract class InternalArangoEdgeCollection extends ArangoExecuteable { private static final String EDGE_PATH = "edge"; private static final String EDGE_JSON_POINTER = "/edge"; - private final ArangoGraphImpl graph; + private final String dbName; + private final String graphName; private final String name; - protected InternalArangoEdgeCollection(final ArangoGraphImpl graph, final String name) { - super(graph); - this.graph = graph; + protected InternalArangoEdgeCollection(final ArangoExecuteable executeable, + final String dbName, + final String graphName, + final String name) { + super(executeable); + this.dbName = dbName; + this.graphName = graphName; this.name = name; } - public ArangoGraph graph() { - return graph; - } - public String name() { return name; } protected InternalRequest removeEdgeDefinitionRequest(final EdgeCollectionDropOptions options) { - return request(graph.db().name(), RequestType.DELETE, PATH_API_GHARIAL, graph.name(), "edge", name) + return request(dbName, RequestType.DELETE, PATH_API_GHARIAL, graphName, "edge", name) .putQueryParam("waitForSync", options.getWaitForSync()) .putQueryParam("dropCollections", options.getDropCollections()); } protected InternalRequest insertEdgeRequest(final T value, final EdgeCreateOptions options) { - final InternalRequest request = request(graph.db().name(), RequestType.POST, PATH_API_GHARIAL, graph.name(), EDGE_PATH, + final InternalRequest request = request(dbName, RequestType.POST, PATH_API_GHARIAL, graphName, EDGE_PATH, name); final EdgeCreateOptions params = (options != null ? options : new EdgeCreateOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -76,7 +76,7 @@ protected ResponseDeserializer insertEdgeResponseDeserializer() { } protected InternalRequest getEdgeRequest(final String key, final GraphDocumentReadOptions options) { - final InternalRequest request = request(graph.db().name(), RequestType.GET, PATH_API_GHARIAL, graph.name(), EDGE_PATH, + final InternalRequest request = request(dbName, RequestType.GET, PATH_API_GHARIAL, graphName, EDGE_PATH, DocumentUtil.createDocumentHandle(name, key)); final GraphDocumentReadOptions params = (options != null ? options : new GraphDocumentReadOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -93,7 +93,7 @@ protected ResponseDeserializer getEdgeResponseDeserializer(final Class } protected InternalRequest replaceEdgeRequest(final String key, final T value, final EdgeReplaceOptions options) { - final InternalRequest request = request(graph.db().name(), RequestType.PUT, PATH_API_GHARIAL, graph.name(), EDGE_PATH, + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_GHARIAL, graphName, EDGE_PATH, DocumentUtil.createDocumentHandle(name, key)); final EdgeReplaceOptions params = (options != null ? options : new EdgeReplaceOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -109,7 +109,7 @@ protected ResponseDeserializer replaceEdgeResponseDeserializer protected InternalRequest updateEdgeRequest(final String key, final T value, final EdgeUpdateOptions options) { final InternalRequest request; - request = request(graph.db().name(), RequestType.PATCH, PATH_API_GHARIAL, graph.name(), EDGE_PATH, + request = request(dbName, RequestType.PATCH, PATH_API_GHARIAL, graphName, EDGE_PATH, DocumentUtil.createDocumentHandle(name, key)); final EdgeUpdateOptions params = (options != null ? options : new EdgeUpdateOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); @@ -125,7 +125,7 @@ protected ResponseDeserializer updateEdgeResponseDeserializer( } protected InternalRequest deleteEdgeRequest(final String key, final EdgeDeleteOptions options) { - final InternalRequest request = request(graph.db().name(), RequestType.DELETE, PATH_API_GHARIAL, graph.name(), EDGE_PATH, + final InternalRequest request = request(dbName, RequestType.DELETE, PATH_API_GHARIAL, graphName, EDGE_PATH, DocumentUtil.createDocumentHandle(name, key)); final EdgeDeleteOptions params = (options != null ? options : new EdgeDeleteOptions()); request.putHeaderParam(TRANSACTION_ID, params.getStreamTransactionId()); From b3bb840c8bfdec978a25117a778bfa948369ef60 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 16 Oct 2023 11:26:08 +0200 Subject: [PATCH 27/62] ArangoGraphAsyncTest --- .../java/com/arangodb/ArangoGraphAsync.java | 14 +- .../internal/ArangoGraphAsyncImpl.java | 8 +- .../com/arangodb/ArangoGraphAsyncTest.java | 498 ++++++++++++++++++ 3 files changed, 509 insertions(+), 11 deletions(-) create mode 100644 driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java diff --git a/core/src/main/java/com/arangodb/ArangoGraphAsync.java b/core/src/main/java/com/arangodb/ArangoGraphAsync.java index 9b0974643..2232c64ad 100644 --- a/core/src/main/java/com/arangodb/ArangoGraphAsync.java +++ b/core/src/main/java/com/arangodb/ArangoGraphAsync.java @@ -99,13 +99,13 @@ public interface ArangoGraphAsync extends ArangoSerdeAccessor { */ ArangoVertexCollectionAsync vertexCollection(String name); -// /** -// * Returns a {@code ArangoEdgeCollectionAsync} instance for the given edge collection name. -// * -// * @param name Name of the edge collection -// * @return collection handler -// */ -// ArangoEdgeCollectionAsync edgeCollection(String name); + /** + * Returns a {@code ArangoEdgeCollectionAsync} instance for the given edge collection name. + * + * @param name Name of the edge collection + * @return collection handler + */ + ArangoEdgeCollectionAsync edgeCollection(String name); /** * Asynchronous version of {@link ArangoGraph#getEdgeDefinitions()} diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java index d4ae5c44d..966a5cd7b 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java @@ -106,10 +106,10 @@ public ArangoVertexCollectionAsync vertexCollection(final String name) { return new ArangoVertexCollectionAsyncImpl(this, name); } -// @Override -// public ArangoEdgeCollection edgeCollection(final String name) { -// return new ArangoEdgeCollectionImpl(this, name); -// } + @Override + public ArangoEdgeCollectionAsync edgeCollection(final String name) { + return new ArangoEdgeCollectionAsyncImpl(this, name); + } @Override public CompletableFuture> getEdgeDefinitions() { diff --git a/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java new file mode 100644 index 000000000..c2b5260b4 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java @@ -0,0 +1,498 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.CollectionPropertiesEntity; +import com.arangodb.entity.EdgeDefinition; +import com.arangodb.entity.GraphEntity; +import com.arangodb.entity.ReplicationFactor; +import com.arangodb.model.EdgeCollectionDropOptions; +import com.arangodb.model.GraphCreateOptions; +import com.arangodb.model.ReplaceEdgeDefinitionOptions; +import com.arangodb.model.VertexCollectionCreateOptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoGraphAsyncTest extends BaseJunit5 { + + private static final String GRAPH_NAME = "ArangoGraphTest_graph"; + + private static final String VERTEX_COL_1 = rndName(); + private static final String VERTEX_COL_2 = rndName(); + private static final String VERTEX_COL_3 = rndName(); + private static final String VERTEX_COL_4 = rndName(); + private static final String VERTEX_COL_5 = rndName(); + + private static final String EDGE_COL_1 = rndName(); + private static final String EDGE_COL_2 = rndName(); + private static final String EDGE_COL_3 = rndName(); + + private static final Integer REPLICATION_FACTOR = 2; + private static final Integer NUMBER_OF_SHARDS = 2; + + private static final EdgeDefinition ed1 = + new EdgeDefinition().collection(EDGE_COL_1).from(VERTEX_COL_1).to(VERTEX_COL_5); + private static final EdgeDefinition ed2 = + new EdgeDefinition().collection(EDGE_COL_2).from(VERTEX_COL_2).to(VERTEX_COL_1, VERTEX_COL_3); + + private static Stream asyncGraphs() { + return asyncDbsStream() + .map(db -> db.graph(GRAPH_NAME)) + .map(Arguments::of); + } + + @BeforeAll + static void init() { + final Collection edgeDefinitions = Arrays.asList(ed1, ed2); + + final GraphCreateOptions options = new GraphCreateOptions() + .replicationFactor(REPLICATION_FACTOR) + .numberOfShards(NUMBER_OF_SHARDS); + + initGraph(GRAPH_NAME, edgeDefinitions, options); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void exists(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + assertThat(graph.exists().get()).isTrue(); + assertThat(graph.db().graph(GRAPH_NAME + "no").exists().get()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createWithReplicationAndWriteConcern(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isCluster()); + + final Collection edgeDefinitions = new ArrayList<>(); + final GraphEntity graph = db.createGraph(GRAPH_NAME + "_1", edgeDefinitions, + new GraphCreateOptions().isSmart(true).replicationFactor(2).writeConcern(2)).get(); + assertThat(graph).isNotNull(); + assertThat(graph.getName()).isEqualTo(GRAPH_NAME + "_1"); + assertThat(graph.getWriteConcern()).isEqualTo(2); + assertThat(graph.getReplicationFactor().get()).isEqualTo(2); + db.graph(GRAPH_NAME + "_1").drop(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getGraphs(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Collection graphs = db.getGraphs().get(); + assertThat(graphs.stream().anyMatch(it -> it.getName().equals(GRAPH_NAME))).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void getInfo(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + final GraphEntity info = graph.getInfo().get(); + assertThat(info).isNotNull(); + assertThat(info.getName()).isEqualTo(GRAPH_NAME); + assertThat(info.getEdgeDefinitions()).hasSize(2); + + assertThat(info.getEdgeDefinitions()) + .anySatisfy(e1 -> { + assertThat(e1.getCollection()).isEqualTo(EDGE_COL_1); + assertThat(e1.getFrom()).contains(VERTEX_COL_1); + assertThat(e1.getTo()).contains(VERTEX_COL_5); + }) + .anySatisfy(e2 -> { + assertThat(e2.getCollection()).isEqualTo(EDGE_COL_2); + assertThat(e2.getFrom()).contains(VERTEX_COL_2); + assertThat(e2.getTo()).contains(VERTEX_COL_1, VERTEX_COL_3); + }); + + assertThat(info.getOrphanCollections()).isEmpty(); + + if (isCluster()) { + for (final String collection : new String[]{EDGE_COL_1, EDGE_COL_2, VERTEX_COL_1, VERTEX_COL_2, VERTEX_COL_5}) { + final CollectionPropertiesEntity properties = graph.db().collection(collection).getProperties().get(); + assertThat(properties.getReplicationFactor().get()).isEqualTo(REPLICATION_FACTOR); + assertThat(properties.getNumberOfShards()).isEqualTo(NUMBER_OF_SHARDS); + } + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void getVertexCollections(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + final Collection vertexCollections = graph.getVertexCollections().get(); + assertThat(vertexCollections) + .hasSize(4) + .contains(VERTEX_COL_1, VERTEX_COL_2, VERTEX_COL_3, VERTEX_COL_5); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void addVertexCollection(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + final GraphEntity g = graph.addVertexCollection(VERTEX_COL_4).get(); + assertThat(g).isNotNull(); + final Collection vertexCollections = graph.getVertexCollections().get(); + assertThat(vertexCollections).contains(VERTEX_COL_1, VERTEX_COL_2, VERTEX_COL_3, VERTEX_COL_4, VERTEX_COL_5); + + // revert + graph.vertexCollection(VERTEX_COL_4).drop().get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void addSatelliteVertexCollection(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster() || isAtLeastVersion(3, 10)); + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 9)); + + String v1Name = "vertex-" + rnd(); + + ArangoGraphAsync g = db.graph(GRAPH_NAME + rnd()); + g.create(null, new GraphCreateOptions().isSmart(true).smartGraphAttribute("test")); + g.addVertexCollection(v1Name, new VertexCollectionCreateOptions().satellites(v1Name)); + + Collection vertexCollections = g.getVertexCollections().get(); + assertThat(vertexCollections).contains(v1Name); + assertThat(db.collection(v1Name).getProperties().get().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + + // revert + g.drop().get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void getEdgeCollections(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + final Collection edgeCollections = graph.getEdgeDefinitions().get(); + assertThat(edgeCollections) + .hasSize(2) + .contains(EDGE_COL_1, EDGE_COL_2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void addEdgeDefinition(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + EdgeDefinition ed = new EdgeDefinition().collection(EDGE_COL_3).from(VERTEX_COL_1).to(VERTEX_COL_2); + final GraphEntity g = graph.addEdgeDefinition(ed).get(); + assertThat(g).isNotNull(); + final Collection edgeDefinitions = g.getEdgeDefinitions(); + assertThat(edgeDefinitions).hasSize(3); + int count = 0; + for (final EdgeDefinition e : edgeDefinitions) { + if (e.getCollection().equals(EDGE_COL_3)) { + count++; + } + } + assertThat(count).isEqualTo(1); + for (final EdgeDefinition e : edgeDefinitions) { + if (e.getCollection().equals(EDGE_COL_3)) { + assertThat(e.getFrom()).contains(VERTEX_COL_1); + assertThat(e.getTo()).contains(VERTEX_COL_2); + } + } + if (isCluster()) { + final CollectionPropertiesEntity properties = graph.db().collection(EDGE_COL_3).getProperties().get(); + assertThat(properties.getReplicationFactor().get()).isEqualTo(REPLICATION_FACTOR); + assertThat(properties.getNumberOfShards()).isEqualTo(NUMBER_OF_SHARDS); + } + + // revert + graph.edgeCollection(EDGE_COL_3).drop().get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void addSatelliteEdgeDefinition(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster() || isAtLeastVersion(3, 10)); + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 9)); + + String eName = "edge-" + rnd(); + String v1Name = "vertex-" + rnd(); + String v2Name = "vertex-" + rnd(); + EdgeDefinition ed = new EdgeDefinition().collection(eName).from(v1Name).to(v2Name).satellites(v1Name); + + ArangoGraphAsync g = db.graph(GRAPH_NAME + rnd()); + g.create(null, new GraphCreateOptions().isSmart(true).smartGraphAttribute("test")); + g.addEdgeDefinition(ed); + final GraphEntity ge = g.getInfo().get(); + assertThat(ge).isNotNull(); + final Collection edgeDefinitions = ge.getEdgeDefinitions(); + assertThat(edgeDefinitions).hasSize(1); + EdgeDefinition e = edgeDefinitions.iterator().next(); + assertThat(e.getCollection()).isEqualTo(eName); + assertThat(e.getFrom()).contains(v1Name); + assertThat(e.getTo()).contains(v2Name); + + assertThat(db.collection(v1Name).getProperties().get().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + + // revert + g.drop().get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void replaceEdgeDefinition(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + final GraphEntity g = graph + .replaceEdgeDefinition(new EdgeDefinition().collection(EDGE_COL_1).from(VERTEX_COL_3).to(VERTEX_COL_4)).get(); + final Collection edgeDefinitions = g.getEdgeDefinitions(); + assertThat(edgeDefinitions).hasSize(2); + int count = 0; + for (final EdgeDefinition e : edgeDefinitions) { + if (e.getCollection().equals(EDGE_COL_1)) { + count++; + } + } + assertThat(count).isEqualTo(1); + for (final EdgeDefinition e : edgeDefinitions) { + if (e.getCollection().equals(EDGE_COL_1)) { + assertThat(e.getFrom()).contains(VERTEX_COL_3); + assertThat(e.getTo()).contains(VERTEX_COL_4); + } + } + assertThat(graph.db().collection(VERTEX_COL_1).exists().get()).isTrue(); + + // revert + graph.edgeCollection(EDGE_COL_1).drop().get(); + graph.vertexCollection(VERTEX_COL_4).drop().get(); + graph.addEdgeDefinition(ed1).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + @Disabled + // FIXME: with dropCollections=true the vertex collections remain in the graph as orphan and not dropped + void replaceEdgeDefinitionDropCollections(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + final GraphEntity g = graph + .replaceEdgeDefinition(new EdgeDefinition().collection(EDGE_COL_1).from(VERTEX_COL_3).to(VERTEX_COL_4), + new ReplaceEdgeDefinitionOptions().waitForSync(true).dropCollections(true)).get(); + final Collection edgeDefinitions = g.getEdgeDefinitions(); + assertThat(edgeDefinitions).hasSize(2); + int count = 0; + for (final EdgeDefinition e : edgeDefinitions) { + if (e.getCollection().equals(EDGE_COL_1)) { + count++; + } + } + assertThat(count).isEqualTo(1); + for (final EdgeDefinition e : edgeDefinitions) { + if (e.getCollection().equals(EDGE_COL_1)) { + assertThat(e.getFrom()).contains(VERTEX_COL_3); + assertThat(e.getTo()).contains(VERTEX_COL_4); + } + } + assertThat(graph.db().collection(VERTEX_COL_5).exists().get()).isFalse(); + + // revert + graph.edgeCollection(EDGE_COL_1).drop().get(); + graph.vertexCollection(VERTEX_COL_4).drop().get(); + graph.addEdgeDefinition(ed1).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void removeEdgeDefinition(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + graph.edgeCollection(EDGE_COL_1).drop().get(); + Collection edgeDefinitions = graph.getEdgeDefinitions().get(); + assertThat(edgeDefinitions).hasSize(1); + assertThat(edgeDefinitions.iterator().next()).isEqualTo(EDGE_COL_2); + assertThat(graph.db().collection(EDGE_COL_1).exists().get()).isTrue(); + + //revert + graph.addEdgeDefinition(ed1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncGraphs") + void removeEdgeDefinitionDropCollections(ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + graph.edgeCollection(EDGE_COL_1).drop(new EdgeCollectionDropOptions() + .dropCollections(true) + .waitForSync(true)).get(); + Collection edgeDefinitions = graph.getEdgeDefinitions().get(); + assertThat(edgeDefinitions).hasSize(1); + assertThat(edgeDefinitions.iterator().next()).isEqualTo(EDGE_COL_2); + assertThat(graph.db().collection(EDGE_COL_1).exists().get()).isFalse(); + + //revert + graph.addEdgeDefinition(ed1).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void smartGraph(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster() || isAtLeastVersion(3, 10)); + + final Collection edgeDefinitions = new ArrayList<>(); + edgeDefinitions.add(new EdgeDefinition().collection("smartGraph-edge-" + rnd()).from("smartGraph-vertex-" + rnd()).to("smartGraph-vertex-" + rnd())); + + String graphId = GRAPH_NAME + rnd(); + final GraphEntity g = db.createGraph(graphId, edgeDefinitions, + new GraphCreateOptions().isSmart(true).smartGraphAttribute("test").numberOfShards(2)).get(); + + assertThat(g).isNotNull(); + assertThat(g.getIsSmart()).isTrue(); + assertThat(g.getSmartGraphAttribute()).isEqualTo("test"); + assertThat(g.getNumberOfShards()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void hybridSmartGraph(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster() || isAtLeastVersion(3, 10)); + assumeTrue((isAtLeastVersion(3, 9))); + + final Collection edgeDefinitions = new ArrayList<>(); + String eName = "hybridSmartGraph-edge-" + rnd(); + String v1Name = "hybridSmartGraph-vertex-" + rnd(); + String v2Name = "hybridSmartGraph-vertex-" + rnd(); + edgeDefinitions.add(new EdgeDefinition().collection(eName).from(v1Name).to(v2Name)); + + String graphId = GRAPH_NAME + rnd(); + final GraphEntity g = db.createGraph(graphId, edgeDefinitions, new GraphCreateOptions() + .satellites(eName, v1Name) + .isSmart(true).smartGraphAttribute("test").replicationFactor(2).numberOfShards(2)).get(); + + assertThat(g).isNotNull(); + assertThat(g.getIsSmart()).isTrue(); + assertThat(g.getSmartGraphAttribute()).isEqualTo("test"); + assertThat(g.getNumberOfShards()).isEqualTo(2); + + assertThat(db.collection(eName).getProperties().get().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + assertThat(db.collection(v1Name).getProperties().get().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + assertThat(db.collection(v2Name).getProperties().get().getReplicationFactor().get()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void disjointSmartGraph(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster() || isAtLeastVersion(3, 10)); + assumeTrue((isAtLeastVersion(3, 7))); + + final Collection edgeDefinitions = new ArrayList<>(); + edgeDefinitions.add(new EdgeDefinition().collection("smartGraph-edge-" + rnd()).from("smartGraph-vertex-" + rnd()).to("smartGraph-vertex-" + rnd())); + + String graphId = GRAPH_NAME + rnd(); + final GraphEntity g = db.createGraph(graphId, edgeDefinitions, new GraphCreateOptions() + .isSmart(true).isDisjoint(true).smartGraphAttribute("test").numberOfShards(2)).get(); + + assertThat(g).isNotNull(); + assertThat(g.getIsSmart()).isTrue(); + assertThat(g.getIsDisjoint()).isTrue(); + assertThat(g.getSmartGraphAttribute()).isEqualTo("test"); + assertThat(g.getNumberOfShards()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void hybridDisjointSmartGraph(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster() || isAtLeastVersion(3, 10)); + assumeTrue((isAtLeastVersion(3, 9))); + + final Collection edgeDefinitions = new ArrayList<>(); + String eName = "hybridDisjointSmartGraph-edge-" + rnd(); + String v1Name = "hybridDisjointSmartGraph-vertex-" + rnd(); + String v2Name = "hybridDisjointSmartGraph-vertex-" + rnd(); + edgeDefinitions.add(new EdgeDefinition().collection(eName).from(v1Name).to(v2Name)); + + String graphId = GRAPH_NAME + rnd(); + final GraphEntity g = db.createGraph(graphId, edgeDefinitions, new GraphCreateOptions() + .satellites(v1Name) + .isSmart(true).isDisjoint(true).smartGraphAttribute("test").replicationFactor(2).numberOfShards(2)).get(); + + assertThat(g).isNotNull(); + assertThat(g.getIsSmart()).isTrue(); + assertThat(g.getIsDisjoint()).isTrue(); + assertThat(g.getSmartGraphAttribute()).isEqualTo("test"); + assertThat(g.getNumberOfShards()).isEqualTo(2); + + assertThat(db.collection(v1Name).getProperties().get().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + assertThat(db.collection(v2Name).getProperties().get().getReplicationFactor().get()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void enterpriseGraph(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster() || isAtLeastVersion(3, 10)); + + final Collection edgeDefinitions = new ArrayList<>(); + edgeDefinitions.add(new EdgeDefinition().collection("enterpriseGraph-edge-" + rnd()).from("enterpriseGraph-vertex-" + rnd()).to("enterpriseGraph-vertex-" + rnd())); + + String graphId = GRAPH_NAME + rnd(); + final GraphEntity g = db.createGraph(graphId, edgeDefinitions, new GraphCreateOptions().isSmart(true).numberOfShards(2)).get(); + + assertThat(g).isNotNull(); + assertThat(g.getSmartGraphAttribute()).isNull(); + assertThat(g.getNumberOfShards()).isEqualTo(2); + if (isAtLeastVersion(3, 10)) { + assertThat(g.getIsSmart()).isTrue(); + } else { + assertThat(g.getIsSmart()).isFalse(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void drop(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final String edgeCollection = "edge_" + rnd(); + final String vertexCollection = "vertex_" + rnd(); + final String graphId = GRAPH_NAME + rnd(); + final GraphEntity result = db.graph(graphId).create(Collections + .singleton(new EdgeDefinition().collection(edgeCollection).from(vertexCollection).to(vertexCollection))).get(); + assertThat(result).isNotNull(); + db.graph(graphId).drop(); + assertThat(db.collection(edgeCollection).exists().get()).isTrue(); + assertThat(db.collection(vertexCollection).exists().get()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void dropPlusDropCollections(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final String edgeCollection = "edge_dropC" + rnd(); + final String vertexCollection = "vertex_dropC" + rnd(); + final String graphId = GRAPH_NAME + "_dropC" + rnd(); + final GraphEntity result = db.graph(graphId).create(Collections + .singleton(new EdgeDefinition().collection(edgeCollection).from(vertexCollection).to(vertexCollection))).get(); + assertThat(result).isNotNull(); + db.graph(graphId).drop(true).get(); + assertThat(db.collection(edgeCollection).exists().get()).isFalse(); + assertThat(db.collection(vertexCollection).exists().get()).isFalse(); + } + +} From 66955d43b1334ccde616536238758766f16c4eee Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 16 Oct 2023 11:37:30 +0200 Subject: [PATCH 28/62] ArangoEdgeCollectionAsyncTest --- .../ArangoEdgeCollectionAsyncTest.java | 446 ++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 driver/src/test/java/com/arangodb/ArangoEdgeCollectionAsyncTest.java diff --git a/driver/src/test/java/com/arangodb/ArangoEdgeCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoEdgeCollectionAsyncTest.java new file mode 100644 index 000000000..b188a007d --- /dev/null +++ b/driver/src/test/java/com/arangodb/ArangoEdgeCollectionAsyncTest.java @@ -0,0 +1,446 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.model.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoEdgeCollectionAsyncTest extends BaseJunit5 { + + private static final String GRAPH_NAME = "EdgeCollectionTest_graph"; + private static final String VERTEX_COLLECTION_NAME = rndName(); + private static final String EDGE_COLLECTION_NAME = rndName(); + + private static Stream asyncArgs() { + return asyncDbsStream() + .map(db -> new Object[]{ + db.graph(GRAPH_NAME).vertexCollection(VERTEX_COLLECTION_NAME), + db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + }) + .map(Arguments::of); + } + + @BeforeAll + static void init() { + initCollections(VERTEX_COLLECTION_NAME); + initEdgeCollections(EDGE_COLLECTION_NAME); + initGraph( + GRAPH_NAME, + Collections.singletonList(new EdgeDefinition() + .collection(EDGE_COLLECTION_NAME) + .from(VERTEX_COLLECTION_NAME) + .to(VERTEX_COLLECTION_NAME) + ), + null + ); + } + + private BaseEdgeDocument createEdgeValue(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final VertexEntity v1 = vertices.insertVertex(new BaseDocument()).get(); + final VertexEntity v2 = vertices.insertVertex(new BaseDocument()).get(); + + final BaseEdgeDocument value = new BaseEdgeDocument(); + value.setFrom(v1.getId()); + value.setTo(v2.getId()); + return value; + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void insertEdge(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value).get(); + assertThat(edge).isNotNull(); + final BaseEdgeDocument document = edges.getEdge(edge.getKey(), BaseEdgeDocument.class).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + assertThat(document.getFrom()).isNotNull(); + assertThat(document.getTo()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void insertEdgeUpdateRev(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value).get(); + assertThat(value.getRevision()).isNull(); + assertThat(edge.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void insertEdgeViolatingUniqueConstraint(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + edges.graph().db().collection(EDGE_COLLECTION_NAME) + .ensurePersistentIndex(Arrays.asList("_from", "_to"), new PersistentIndexOptions().unique(true)).get(); + + BaseEdgeDocument edge = createEdgeValue(vertices); + edges.insertEdge(edge).get(); + + try { + edges.insertEdge(edge).get(); + } catch (ArangoDBException e) { + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1210); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void getEdge(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value).get(); + final BaseEdgeDocument document = edges + .getEdge(edge.getKey(), BaseEdgeDocument.class).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + assertThat(document.getFrom()).isNotNull(); + assertThat(document.getTo()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void getEdgeIfMatch(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch(edge.getRev()); + final BaseDocument document = edges.getEdge(edge.getKey(), + BaseDocument.class, options).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void getEdgeIfMatchFail(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch("no"); + final BaseEdgeDocument edge2 = edges.getEdge(edge.getKey(), + BaseEdgeDocument.class, options).get(); + assertThat(edge2).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void getEdgeIfNoneMatch(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch("no"); + final BaseDocument document = edges.getEdge(edge.getKey(), + BaseDocument.class, options).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void getEdgeIfNoneMatchFail(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch(edge.getRev()); + final BaseEdgeDocument edge2 = edges.getEdge(edge.getKey(), + BaseEdgeDocument.class, options).get(); + assertThat(edge2).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void replaceEdge(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final EdgeUpdateEntity replaceResult = edges + .replaceEdge(createResult.getKey(), doc).get(); + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getRev()).isNotEqualTo(replaceResult.getOldRev()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseEdgeDocument readResult = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getRevision()).isEqualTo(replaceResult.getRev()); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void replaceEdgeUpdateRev(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + final EdgeUpdateEntity replaceResult = edges + .replaceEdge(createResult.getKey(), doc).get(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(replaceResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void replaceEdgeIfMatch(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final EdgeReplaceOptions options = new EdgeReplaceOptions().ifMatch(createResult.getRev()); + final EdgeUpdateEntity replaceResult = edges + .replaceEdge(createResult.getKey(), doc, options).get(); + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getRev()).isNotEqualTo(replaceResult.getOldRev()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseEdgeDocument readResult = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getRevision()).isEqualTo(replaceResult.getRev()); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void replaceEdgeIfMatchFail(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final EdgeReplaceOptions options = new EdgeReplaceOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> edges.replaceEdge(createResult.getKey(), doc, options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void updateEdge(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseEdgeDocument readResult = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getAttribute("a")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("a"))).isEqualTo("test1"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + assertThat(readResult.getRevision()).isEqualTo(updateResult.getRev()); + assertThat(readResult.getProperties()).containsKey("c"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void updateEdgeUpdateRev(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc).get(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(updateResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void updateEdgeIfMatch(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().ifMatch(createResult.getRev()); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc, options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseEdgeDocument readResult = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getAttribute("a")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("a"))).isEqualTo("test1"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + assertThat(readResult.getRevision()).isEqualTo(updateResult.getRev()); + assertThat(readResult.getProperties()).containsKey("c"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void updateEdgeIfMatchFail(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> edges.updateEdge(createResult.getKey(), doc, options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void updateEdgeKeepNullTrue(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + doc.updateAttribute("a", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().keepNull(true); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc, options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseEdgeDocument readResult = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties().keySet()).hasSize(6); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void updateEdgeKeepNullFalse(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + doc.updateAttribute("a", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().keepNull(false); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc, options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseEdgeDocument readResult = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getRevision()).isNotNull(); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void deleteEdge(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + edges.deleteEdge(createResult.getKey()).get(); + final BaseEdgeDocument edge = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); + assertThat(edge).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void deleteEdgeIfMatch(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + final EdgeDeleteOptions options = new EdgeDeleteOptions().ifMatch(createResult.getRev()); + edges.deleteEdge(createResult.getKey(), options); + final BaseEdgeDocument edge = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); + assertThat(edge).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void deleteEdgeIfMatchFail(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc).get(); + final EdgeDeleteOptions options = new EdgeDeleteOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> edges.deleteEdge(createResult.getKey(), options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArgs") + void edgeKeyWithSpecialChars(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectionAsync edges) throws ExecutionException, InterruptedException { + final BaseEdgeDocument value = createEdgeValue(vertices); + final String key = "_-:.@()+,=;$!*'%" + UUID.randomUUID(); + value.setKey(key); + final EdgeEntity edge = edges.insertEdge(value).get(); + assertThat(edge).isNotNull(); + final BaseEdgeDocument document = edges.getEdge(edge.getKey(), BaseEdgeDocument.class).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(key); + assertThat(document.getFrom()).isNotNull(); + assertThat(document.getTo()).isNotNull(); + } + +} From 0ecfee39cacf5c2059854cd96146ac885f486d67 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 16 Oct 2023 11:47:22 +0200 Subject: [PATCH 29/62] ArangoVertexCollectionAsyncTest --- .../ArangoVertexCollectionAsyncTest.java | 496 ++++++++++++++++++ 1 file changed, 496 insertions(+) create mode 100644 driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java diff --git a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java new file mode 100644 index 000000000..b2d09bf58 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java @@ -0,0 +1,496 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.BaseDocument; +import com.arangodb.entity.VertexEntity; +import com.arangodb.entity.VertexUpdateEntity; +import com.arangodb.model.*; +import com.arangodb.util.RawJson; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoVertexCollectionAsyncTest extends BaseJunit5 { + + private static final String GRAPH_NAME = "ArangoVertexCollectionTest_graph"; + private static final String COLLECTION_NAME = rndName(); + + private static Stream asyncVertices() { + return asyncDbsStream() + .map(db -> db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME)) + .map(Arguments::of); + } + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + initGraph( + GRAPH_NAME, + null, + new GraphCreateOptions().orphanCollections(COLLECTION_NAME) + ); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void dropVertexCollection(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + ArangoGraphAsync graph = vertices.graph(); + vertices.drop().get(); + final Collection vertexCollections = graph.getVertexCollections().get(); + assertThat(vertexCollections).isEmpty(); + assertThat(graph.db().collection(COLLECTION_NAME).exists().get()).isTrue(); + + // revert + graph.addVertexCollection(COLLECTION_NAME).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void dropVertexCollectionDropCollectionTrue(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + ArangoGraphAsync graph = vertices.graph(); + vertices.drop(new VertexCollectionDropOptions().dropCollection(true)).get(); + final Collection vertexCollections = graph.getVertexCollections().get(); + assertThat(vertexCollections).isEmpty(); + assertThat(graph.db().collection(COLLECTION_NAME).exists().get()).isFalse(); + + // revert + initCollections(COLLECTION_NAME); + graph.addVertexCollection(COLLECTION_NAME).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void insertVertex(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null).get(); + assertThat(vertex).isNotNull(); + ArangoCollectionAsync collection = vertices.graph().db().collection(vertices.name()); + final BaseDocument document = collection + .getDocument(vertex.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void insertVertexViolatingUniqueConstraint(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = vertices.graph().db().collection(vertices.name()); + collection + .ensurePersistentIndex(Collections.singletonList("field"), + new PersistentIndexOptions().unique(true).sparse(true)); + + VertexEntity inserted = vertices.insertVertex(RawJson.of("{\"field\": 99}")).get(); + + try { + vertices.insertVertex(RawJson.of("{\"field\": 99}")); + } catch (ArangoDBException e) { + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1210); + } + + // revert + vertices.deleteVertex(inserted.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void duplicateInsertSameObjectVertex(ArangoVertexCollectionAsync vertices) { + + // ######################################################### + // Create a new BaseDocument + // ######################################################### + + UUID uuid = UUID.randomUUID(); + BaseDocument bd = new BaseDocument(UUID.randomUUID().toString()); + bd.setKey(uuid.toString()); + bd.addAttribute("name", "Paul"); + + vertices.insertVertex(bd); + + UUID uuid2 = UUID.randomUUID(); + BaseDocument bd2 = new BaseDocument(UUID.randomUUID().toString()); + bd2.setKey(uuid2.toString()); + bd2.addAttribute("name", "Paul"); + + vertices.insertVertex(bd2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void insertVertexUpdateRev(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity vertex = vertices.insertVertex(doc, null).get(); + assertThat(doc.getRevision()).isNull(); + assertThat(vertex.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void getVertex(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null).get(); + final BaseDocument document = vertices + .getVertex(vertex.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void getVertexIfMatch(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch(vertex.getRev()); + final BaseDocument document = vertices + .getVertex(vertex.getKey(), BaseDocument.class, options).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void getVertexIfMatchFail(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch("no"); + final BaseDocument vertex2 = vertices + .getVertex(vertex.getKey(), BaseDocument.class, options).get(); + assertThat(vertex2).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void getVertexIfNoneMatch(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch("no"); + final BaseDocument document = vertices + .getVertex(vertex.getKey(), BaseDocument.class, options).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void getVertexIfNoneMatchFail(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch(vertex.getRev()); + final BaseDocument vertex2 = vertices + .getVertex(vertex.getKey(), BaseDocument.class, options).get(); + assertThat(vertex2).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void replaceVertex(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final VertexUpdateEntity replaceResult = vertices + .replaceVertex(createResult.getKey(), doc, null).get(); + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getRev()).isNotEqualTo(replaceResult.getOldRev()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getRevision()).isEqualTo(replaceResult.getRev()); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void replaceVertexUpdateRev(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + final VertexUpdateEntity replaceResult = vertices + .replaceVertex(createResult.getKey(), doc, null).get(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(replaceResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void replaceVertexIfMatch(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final VertexReplaceOptions options = new VertexReplaceOptions().ifMatch(createResult.getRev()); + final VertexUpdateEntity replaceResult = vertices + .replaceVertex(createResult.getKey(), doc, options).get(); + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getRev()).isNotEqualTo(replaceResult.getOldRev()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getRevision()).isEqualTo(replaceResult.getRev()); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void replaceVertexIfMatchFail(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final VertexReplaceOptions options = new VertexReplaceOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> vertices.replaceVertex(createResult.getKey(), doc, options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void updateVertex(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, null).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getAttribute("a")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("a"))).isEqualTo("test1"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + assertThat(readResult.getRevision()).isEqualTo(updateResult.getRev()); + assertThat(readResult.getProperties()).containsKey("c"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void updateVertexUpdateRev(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.addAttribute("foo", "bar"); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, null).get(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(updateResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void updateVertexIfMatch(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final VertexUpdateOptions options = new VertexUpdateOptions().ifMatch(createResult.getRev()); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getAttribute("a")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("a"))).isEqualTo("test1"); + assertThat(readResult.getAttribute("b")).isNotNull(); + assertThat(String.valueOf(readResult.getAttribute("b"))).isEqualTo("test"); + assertThat(readResult.getRevision()).isEqualTo(updateResult.getRev()); + assertThat(readResult.getProperties()).containsKey("c"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void updateVertexIfMatchFail(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final VertexUpdateOptions options = new VertexUpdateOptions().ifMatch("no"); + + Throwable thrown = catchThrowable(() -> vertices.updateVertex(createResult.getKey(), doc, options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void updateVertexKeepNullTrue(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.updateAttribute("a", null); + final VertexUpdateOptions options = new VertexUpdateOptions().keepNull(true); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties().keySet()).hasSize(4); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void updateVertexKeepNullFalse(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + doc.updateAttribute("a", null); + final VertexUpdateOptions options = new VertexUpdateOptions().keepNull(false); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, options).get(); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + + final BaseDocument readResult = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getRevision()).isNotNull(); + assertThat(readResult.getProperties().keySet()).doesNotContain("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void deleteVertex(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + vertices.deleteVertex(createResult.getKey(), null).get(); + final BaseDocument vertex = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(vertex).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void deleteVertexIfMatch(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + final VertexDeleteOptions options = new VertexDeleteOptions().ifMatch(createResult.getRev()); + vertices.deleteVertex(createResult.getKey(), options).get(); + final BaseDocument vertex = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(vertex).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void deleteVertexIfMatchFail(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null).get(); + final VertexDeleteOptions options = new VertexDeleteOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> vertices.deleteVertex(createResult.getKey(), options).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void vertexKeyWithSpecialChars(ArangoVertexCollectionAsync vertices) throws ExecutionException, InterruptedException { + final String key = "_-:.@()+,=;$!*'%" + UUID.randomUUID(); + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(key), null).get(); + assertThat(vertex).isNotNull(); + ArangoCollectionAsync collection = vertices.graph().db().collection(vertices.name()); + final BaseDocument document = collection + .getDocument(vertex.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(key); + } + +} From 5fc306ea9e5af43d7a8135b7c37c66704a304c98 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 16 Oct 2023 14:11:49 +0200 Subject: [PATCH 30/62] ArangoViewAsync --- .../com/arangodb/ArangoDatabaseAsync.java | 18 ++- .../java/com/arangodb/ArangoViewAsync.java | 68 ++++++++++ .../internal/ArangoDatabaseAsyncImpl.java | 8 +- .../arangodb/internal/ArangoSearchImpl.java | 8 ++ .../internal/ArangoViewAsyncImpl.java | 75 ++++++++++ .../com/arangodb/internal/ArangoViewImpl.java | 11 +- .../internal/InternalArangoSearch.java | 10 +- .../arangodb/internal/InternalArangoView.java | 21 ++- .../internal/InternalSearchAlias.java | 10 +- .../arangodb/internal/SearchAliasImpl.java | 9 +- .../com/arangodb/ArangoViewAsyncTest.java | 128 ++++++++++++++++++ 11 files changed, 332 insertions(+), 34 deletions(-) create mode 100644 core/src/main/java/com/arangodb/ArangoViewAsync.java create mode 100644 core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java create mode 100644 driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java diff --git a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java index 09c049427..1b3523d59 100644 --- a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java @@ -296,20 +296,24 @@ public interface ArangoDatabaseAsync extends ArangoSerdeAccessor { */ CompletableFuture> getViews(); -// /** -// * Asynchronous version of {@link ArangoDatabase#view(String)} -// */ -// CompletableFuture view(String name); -// + /** + * Returns a {@code ArangoViewAsync} instance for the given view name. + * + * @param name Name of the view + * @return view handler + * @since ArangoDB 3.4.0 + */ + ArangoViewAsync view(String name); + // /** // * Asynchronous version of {@link ArangoDatabase#arangoSearch(String)} // */ -// CompletableFuture arangoSearch(String name); +// ArangoSearchAsync arangoSearch(String name); // // /** // * Asynchronous version of {@link ArangoDatabase#searchAlias(String)} // */ -// CompletableFuture searchAlias(String name); +// SearchAliasAsync searchAlias(String name); /** * Asynchronous version of {@link ArangoDatabase#createView(String, ViewType)} diff --git a/core/src/main/java/com/arangodb/ArangoViewAsync.java b/core/src/main/java/com/arangodb/ArangoViewAsync.java new file mode 100644 index 000000000..57cf7f2a8 --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoViewAsync.java @@ -0,0 +1,68 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.ViewEntity; + +import javax.annotation.concurrent.ThreadSafe; +import java.util.concurrent.CompletableFuture; + +/** + * Asynchronous version of {@link ArangoView} + */ +@ThreadSafe +public interface ArangoViewAsync extends ArangoSerdeAccessor { + + /** + * The handler of the database the collection is within + * + * @return database handler + */ + ArangoDatabaseAsync db(); + + /** + * The name of the view + * + * @return view name + */ + String name(); + + /** + * Asynchronous version of {@link ArangoView#exists()} + */ + CompletableFuture exists(); + + /** + * Asynchronous version of {@link ArangoView#drop()} + */ + CompletableFuture drop(); + + /** + * Asynchronous version of {@link ArangoView#rename(String)} + */ + CompletableFuture rename(String newName); + + /** + * Asynchronous version of {@link ArangoView#getInfo()} + */ + CompletableFuture getInfo(); + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index 440ca1e61..85c5086b0 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -374,10 +374,10 @@ public CompletableFuture> getViews() { return executorAsync().execute(getViewsRequest(), getViewsResponseDeserializer()); } -// @Override -// public CompletableFuture view(final String name) { -// return new ArangoViewImpl(this, name); -// } + @Override + public ArangoViewAsync view(final String name) { + return new ArangoViewAsyncImpl(this, name); + } // @Override // public ArangoSearch arangoSearch(final String name) { diff --git a/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java b/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java index 12610654e..db616c303 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java @@ -21,6 +21,7 @@ package com.arangodb.internal; import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabase; import com.arangodb.ArangoSearch; import com.arangodb.entity.ViewEntity; import com.arangodb.entity.arangosearch.ArangoSearchPropertiesEntity; @@ -31,9 +32,16 @@ * @author Mark Vollmary */ public class ArangoSearchImpl extends InternalArangoSearch implements ArangoSearch { + private final ArangoDatabase db; protected ArangoSearchImpl(final ArangoDatabaseImpl db, final String name) { super(db, name); + this.db = db; + } + + @Override + public ArangoDatabase db() { + return db; } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java new file mode 100644 index 000000000..800c1ad43 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java @@ -0,0 +1,75 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.*; +import com.arangodb.entity.ViewEntity; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +/** + * @author Mark Vollmary + */ +public class ArangoViewAsyncImpl extends InternalArangoView implements ArangoViewAsync { + private final ArangoDatabaseAsyncImpl db; + protected ArangoViewAsyncImpl(final ArangoDatabaseAsyncImpl db, final String name) { + super(db, db.name(), name); + this.db = db; + } + + @Override + public ArangoDatabaseAsync db() { + return db; + } + + @Override + public CompletableFuture exists() { + return getInfo() + .thenApply(Objects::nonNull) + .exceptionally(e -> { + if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e.getCause(); + if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(aEx.getErrorNum())) { + return false; + } + } + throw new CompletionException(e); + }); + } + + @Override + public CompletableFuture drop() { + return executorAsync().execute(dropRequest(), Void.class); + } + + @Override + public CompletableFuture rename(final String newName) { + return executorAsync().execute(renameRequest(newName), ViewEntity.class); + } + + @Override + public CompletableFuture getInfo() { + return executorAsync().execute(getInfoRequest(), ViewEntity.class); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java b/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java index d5e0bf2ed..b31e17610 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoViewImpl.java @@ -21,6 +21,7 @@ package com.arangodb.internal; import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabase; import com.arangodb.ArangoView; import com.arangodb.entity.ViewEntity; @@ -28,9 +29,15 @@ * @author Mark Vollmary */ public class ArangoViewImpl extends InternalArangoView implements ArangoView { - + private final ArangoDatabase db; protected ArangoViewImpl(final ArangoDatabaseImpl db, final String name) { - super(db, name); + super(db, db.name(), name); + this.db = db; + } + + @Override + public ArangoDatabase db() { + return db; } @Override diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java b/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java index 90b73c445..a262d7232 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java @@ -28,23 +28,25 @@ public class InternalArangoSearch extends InternalArangoView { private static final String PROPERTIES_PATH = "properties"; + private final String dbName; protected InternalArangoSearch(final ArangoDatabaseImpl db, final String name) { - super(db, name); + super(db, db.name(), name); + dbName = db.name(); } protected InternalRequest getPropertiesRequest() { - return request(db.name(), RequestType.GET, PATH_API_VIEW, name, PROPERTIES_PATH); + return request(dbName, RequestType.GET, PATH_API_VIEW, name, PROPERTIES_PATH); } protected InternalRequest replacePropertiesRequest(final ArangoSearchPropertiesOptions options) { - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_VIEW, name, PROPERTIES_PATH); + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_VIEW, name, PROPERTIES_PATH); request.setBody(getSerde().serialize(options != null ? options : new ArangoSearchPropertiesOptions())); return request; } protected InternalRequest updatePropertiesRequest(final ArangoSearchPropertiesOptions options) { - final InternalRequest request = request(db.name(), RequestType.PATCH, PATH_API_VIEW, name, PROPERTIES_PATH); + final InternalRequest request = request(dbName, RequestType.PATCH, PATH_API_VIEW, name, PROPERTIES_PATH); request.setBody(getSerde().serialize(options != null ? options : new ArangoSearchPropertiesOptions())); return request; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoView.java b/core/src/main/java/com/arangodb/internal/InternalArangoView.java index 1aa4ba29d..9ab3cffab 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoView.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoView.java @@ -20,7 +20,6 @@ package com.arangodb.internal; -import com.arangodb.ArangoDatabase; import com.arangodb.model.OptionsBuilder; import com.arangodb.model.ViewRenameOptions; @@ -33,35 +32,33 @@ public abstract class InternalArangoView extends ArangoExecuteable { protected static final String PATH_API_VIEW = "/_api/view"; protected static final String PATH_API_ANALYZER = "/_api/analyzer"; - protected final ArangoDatabaseImpl db; + protected final String dbName; protected final String name; - protected InternalArangoView(final ArangoDatabaseImpl db, final String name) { - super(db); - this.db = db; + protected InternalArangoView(final ArangoExecuteable executeable, + final String dbName, + final String name) { + super(executeable); + this.dbName = dbName; this.name = name; } - public ArangoDatabase db() { - return db; - } - public String name() { return name; } protected InternalRequest dropRequest() { - return request(db.name(), RequestType.DELETE, PATH_API_VIEW, name); + return request(dbName, RequestType.DELETE, PATH_API_VIEW, name); } protected InternalRequest renameRequest(final String newName) { - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_VIEW, name, "rename"); + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_VIEW, name, "rename"); request.setBody(getSerde().serialize(OptionsBuilder.build(new ViewRenameOptions(), newName))); return request; } protected InternalRequest getInfoRequest() { - return request(db.name(), RequestType.GET, PATH_API_VIEW, name); + return request(dbName, RequestType.GET, PATH_API_VIEW, name); } } diff --git a/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java b/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java index ae8aecad8..da540426a 100644 --- a/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java +++ b/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java @@ -25,23 +25,25 @@ public class InternalSearchAlias extends InternalArangoView { private static final String PROPERTIES_PATH = "properties"; + private final String dbName; protected InternalSearchAlias(final ArangoDatabaseImpl db, final String name) { - super(db, name); + super(db, db.name(), name); + dbName = db.name(); } protected InternalRequest getPropertiesRequest() { - return request(db.name(), RequestType.GET, PATH_API_VIEW, name, PROPERTIES_PATH); + return request(dbName, RequestType.GET, PATH_API_VIEW, name, PROPERTIES_PATH); } protected InternalRequest replacePropertiesRequest(final SearchAliasPropertiesOptions options) { - final InternalRequest request = request(db.name(), RequestType.PUT, PATH_API_VIEW, name, PROPERTIES_PATH); + final InternalRequest request = request(dbName, RequestType.PUT, PATH_API_VIEW, name, PROPERTIES_PATH); request.setBody(getSerde().serialize(options != null ? options : new SearchAliasPropertiesOptions())); return request; } protected InternalRequest updatePropertiesRequest(final SearchAliasPropertiesOptions options) { - final InternalRequest request = request(db.name(), RequestType.PATCH, PATH_API_VIEW, name, PROPERTIES_PATH); + final InternalRequest request = request(dbName, RequestType.PATCH, PATH_API_VIEW, name, PROPERTIES_PATH); request.setBody(getSerde().serialize(options != null ? options : new SearchAliasPropertiesOptions())); return request; } diff --git a/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java b/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java index b7839098a..364efdcc1 100644 --- a/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java +++ b/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java @@ -21,6 +21,7 @@ package com.arangodb.internal; import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabase; import com.arangodb.SearchAlias; import com.arangodb.entity.ViewEntity; import com.arangodb.entity.arangosearch.SearchAliasPropertiesEntity; @@ -31,9 +32,15 @@ * @author Michele Rastelli */ public class SearchAliasImpl extends InternalSearchAlias implements SearchAlias { - + private final ArangoDatabase db; protected SearchAliasImpl(final ArangoDatabaseImpl db, final String name) { super(db, name); + this.db = db; + } + + @Override + public ArangoDatabase db() { + return db; } @Override diff --git a/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java new file mode 100644 index 000000000..f36a380c7 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java @@ -0,0 +1,128 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.ViewEntity; +import com.arangodb.entity.ViewType; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Collection; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoViewAsyncTest extends BaseJunit5 { + + @BeforeAll + static void init() { + initDB(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void create(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + String name = rndName(); + db.createView(name, ViewType.ARANGO_SEARCH).get(); + assertThat(db.view(name).exists().get()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createWithNotNormalizedName(ArangoDatabaseAsync db) { + assumeTrue(supportsExtendedNames()); + final String name = "view-\u006E\u0303\u00f1"; + Throwable thrown = catchThrowable(() -> db.createView(name, ViewType.ARANGO_SEARCH)); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .hasMessageContaining("normalized") + .extracting(it -> ((ArangoDBException) it).getResponseCode()).isEqualTo(400); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getInfo(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + String name = rndName(); + db.createView(name, ViewType.ARANGO_SEARCH); + final ViewEntity info = db.view(name).getInfo().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(name); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getInfoSearchAlias(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String name = rndName(); + db.createView(name, ViewType.SEARCH_ALIAS).get(); + final ViewEntity info = db.view(name).getInfo().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(name); + assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getViews(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String name1 = rndName(); + String name2 = rndName(); + db.createView(name1, ViewType.ARANGO_SEARCH).get(); + db.createView(name2, ViewType.SEARCH_ALIAS).get(); + Collection views = db.getViews().get(); + assertThat(views).extracting(ViewEntity::getName).contains(name1, name2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void drop(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + String name = rndName(); + db.createView(name, ViewType.ARANGO_SEARCH).get(); + final ArangoViewAsync view = db.view(name); + view.drop().get(); + assertThat(view.exists().get()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void rename(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + String oldName = rndName(); + String newName = rndName(); + + db.createView(oldName, ViewType.ARANGO_SEARCH); + db.view(oldName).rename(newName).get(); + assertThat(db.view(oldName).exists().get()).isFalse(); + assertThat(db.view(newName).exists().get()).isTrue(); + } + +} From a0147031cf0a0f6bcdbb305203ec30abc8529157 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 17 Oct 2023 11:51:55 +0200 Subject: [PATCH 31/62] fixed wrapping ArangoDBException in async communication --- .../main/java/com/arangodb/ArangoDBException.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/arangodb/ArangoDBException.java b/core/src/main/java/com/arangodb/ArangoDBException.java index ab6d63610..95f1921d0 100644 --- a/core/src/main/java/com/arangodb/ArangoDBException.java +++ b/core/src/main/java/com/arangodb/ArangoDBException.java @@ -75,9 +75,20 @@ public ArangoDBException(Throwable cause, long requestId) { this.requestId = requestId; } + private ArangoDBException(final ArangoDBException e) { + super(e.getMessage(), e); + this.entity = e.entity; + this.responseCode = e.responseCode; + this.requestId = e.requestId; + } + public static ArangoDBException wrap(Throwable t) { if (t instanceof ArangoDBException) { - return (ArangoDBException) t; + if (t.getCause() == null) { + return new ArangoDBException((ArangoDBException) t); + } else { + return wrap(t.getCause()); + } } else { return new ArangoDBException(t); } From b2d4a90e3027e5e445a78aa2fbb6ea326838fce5 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 17 Oct 2023 11:52:10 +0200 Subject: [PATCH 32/62] test fixes --- .../ArangoEdgeCollectionAsyncImpl.java | 19 +++++++++++-- .../internal/ArangoEdgeCollectionImpl.java | 27 ++++++++++++++++--- .../ArangoVertexCollectionAsyncImpl.java | 23 ++++++++++++++-- .../internal/ArangoVertexCollectionImpl.java | 27 ++++++++++++++++--- .../ArangoEdgeCollectionAsyncTest.java | 13 +++++---- .../com/arangodb/ArangoGraphAsyncTest.java | 2 +- .../ArangoVertexCollectionAsyncTest.java | 2 +- .../com/arangodb/ArangoViewAsyncTest.java | 2 +- 8 files changed, 95 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java index f24795f83..f786f1b3c 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java @@ -71,12 +71,27 @@ public CompletableFuture insertEdge(final Object value, final EdgeCr @Override public CompletableFuture getEdge(final String key, final Class type) { return executorAsync().execute(getEdgeRequest(key, new GraphDocumentReadOptions()), - getEdgeResponseDeserializer(type)); + getEdgeResponseDeserializer(type)) + .exceptionally(e -> { + // FIXME + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + }); + } @Override public CompletableFuture getEdge(final String key, final Class type, final GraphDocumentReadOptions options) { - return executorAsync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)); + return executorAsync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)) + .exceptionally(e -> { + // FIXME + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + }); } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java index d2bfebff1..85cfb4765 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionImpl.java @@ -20,17 +20,22 @@ package com.arangodb.internal; +import com.arangodb.ArangoDBException; import com.arangodb.ArangoEdgeCollection; import com.arangodb.ArangoGraph; import com.arangodb.entity.EdgeEntity; import com.arangodb.entity.EdgeUpdateEntity; import com.arangodb.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Mark Vollmary */ public class ArangoEdgeCollectionImpl extends InternalArangoEdgeCollection implements ArangoEdgeCollection { + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoEdgeCollectionImpl.class); + private final ArangoGraphImpl graph; protected ArangoEdgeCollectionImpl(final ArangoGraphImpl graph, final String name) { @@ -66,13 +71,29 @@ public EdgeEntity insertEdge(final Object value, final EdgeCreateOptions options @Override public T getEdge(final String key, final Class type) { - return executorSync().execute(getEdgeRequest(key, new GraphDocumentReadOptions()), - getEdgeResponseDeserializer(type)); + // FIXME + try { + return executorSync().execute(getEdgeRequest(key, new GraphDocumentReadOptions()), + getEdgeResponseDeserializer(type)); + } catch (final ArangoDBException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + } } @Override public T getEdge(final String key, final Class type, final GraphDocumentReadOptions options) { - return executorSync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)); + // FIXME + try { + return executorSync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)); + } catch (final ArangoDBException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + } } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java index 4fa4f3f18..0d946bd67 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java @@ -25,6 +25,8 @@ import com.arangodb.entity.VertexEntity; import com.arangodb.entity.VertexUpdateEntity; import com.arangodb.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.concurrent.CompletableFuture; @@ -33,6 +35,8 @@ */ public class ArangoVertexCollectionAsyncImpl extends InternalArangoVertexCollection implements ArangoVertexCollectionAsync { + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoVertexCollectionAsyncImpl.class); + private final ArangoGraphAsync graph; protected ArangoVertexCollectionAsyncImpl(final ArangoGraphAsyncImpl graph, final String name) { @@ -69,12 +73,27 @@ public CompletableFuture insertVertex(final Object value, final Ve @Override public CompletableFuture getVertex(final String key, final Class type) { return executorAsync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), - getVertexResponseDeserializer(type)); + getVertexResponseDeserializer(type)) + .exceptionally(e -> { + // FIXME + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + }); + } @Override public CompletableFuture getVertex(final String key, final Class type, final GraphDocumentReadOptions options) { - return executorAsync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); + return executorAsync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)) + .exceptionally(e -> { + // FIXME + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + }); } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java index 6bdebf900..813468f1f 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionImpl.java @@ -20,17 +20,22 @@ package com.arangodb.internal; +import com.arangodb.ArangoDBException; import com.arangodb.ArangoGraph; import com.arangodb.ArangoVertexCollection; import com.arangodb.entity.VertexEntity; import com.arangodb.entity.VertexUpdateEntity; import com.arangodb.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Mark Vollmary */ public class ArangoVertexCollectionImpl extends InternalArangoVertexCollection implements ArangoVertexCollection { + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoVertexCollectionImpl.class); + private final ArangoGraph graph; protected ArangoVertexCollectionImpl(final ArangoGraphImpl graph, final String name) { @@ -66,13 +71,29 @@ public VertexEntity insertVertex(final Object value, final VertexCreateOptions o @Override public T getVertex(final String key, final Class type) { - return executorSync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), - getVertexResponseDeserializer(type)); + // FIXME + try { + return executorSync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), + getVertexResponseDeserializer(type)); + } catch (final ArangoDBException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + } } @Override public T getVertex(final String key, final Class type, final GraphDocumentReadOptions options) { - return executorSync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); + // FIXME + try { + return executorSync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)); + } catch (final ArangoDBException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(e.getMessage(), e); + } + return null; + } } @Override diff --git a/driver/src/test/java/com/arangodb/ArangoEdgeCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoEdgeCollectionAsyncTest.java index b188a007d..749082454 100644 --- a/driver/src/test/java/com/arangodb/ArangoEdgeCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoEdgeCollectionAsyncTest.java @@ -112,12 +112,11 @@ void insertEdgeViolatingUniqueConstraint(ArangoVertexCollectionAsync vertices, A BaseEdgeDocument edge = createEdgeValue(vertices); edges.insertEdge(edge).get(); - try { - edges.insertEdge(edge).get(); - } catch (ArangoDBException e) { - assertThat(e.getResponseCode()).isEqualTo(409); - assertThat(e.getErrorNum()).isEqualTo(1210); - } + Throwable t = catchThrowable(() -> edges.insertEdge(edge).get()).getCause(); + assertThat(t).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) t; + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1210); } @ParameterizedTest(name = "{index}") @@ -409,7 +408,7 @@ void deleteEdgeIfMatch(ArangoVertexCollectionAsync vertices, ArangoEdgeCollectio final BaseEdgeDocument doc = createEdgeValue(vertices); final EdgeEntity createResult = edges.insertEdge(doc).get(); final EdgeDeleteOptions options = new EdgeDeleteOptions().ifMatch(createResult.getRev()); - edges.deleteEdge(createResult.getKey(), options); + edges.deleteEdge(createResult.getKey(), options).get(); final BaseEdgeDocument edge = edges .getEdge(createResult.getKey(), BaseEdgeDocument.class).get(); assertThat(edge).isNull(); diff --git a/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java index c2b5260b4..d3dee7cac 100644 --- a/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java @@ -331,7 +331,7 @@ void removeEdgeDefinition(ArangoGraphAsync graph) throws ExecutionException, Int assertThat(graph.db().collection(EDGE_COL_1).exists().get()).isTrue(); //revert - graph.addEdgeDefinition(ed1); + graph.addEdgeDefinition(ed1).get(); } @ParameterizedTest(name = "{index}") diff --git a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java index b2d09bf58..74b86326e 100644 --- a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java @@ -123,7 +123,7 @@ void insertVertexViolatingUniqueConstraint(ArangoVertexCollectionAsync vertices) } // revert - vertices.deleteVertex(inserted.getKey()); + vertices.deleteVertex(inserted.getKey()).get(); } @ParameterizedTest(name = "{index}") diff --git a/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java index f36a380c7..ec804dca7 100644 --- a/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java @@ -69,7 +69,7 @@ void createWithNotNormalizedName(ArangoDatabaseAsync db) { @MethodSource("asyncDbs") void getInfo(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { String name = rndName(); - db.createView(name, ViewType.ARANGO_SEARCH); + db.createView(name, ViewType.ARANGO_SEARCH).get(); final ViewEntity info = db.view(name).getInfo().get(); assertThat(info).isNotNull(); assertThat(info.getId()).isNotNull(); From 0f50eaffc028ea4461d2ef72c8dca3c7d0ce460e Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 17 Oct 2023 14:38:20 +0200 Subject: [PATCH 33/62] ArangoSearchAsync --- .../com/arangodb/ArangoDatabaseAsync.java | 14 ++- .../java/com/arangodb/ArangoSearchAsync.java | 62 +++++++++++ .../internal/ArangoDatabaseAsyncImpl.java | 8 +- .../internal/ArangoSearchAsyncImpl.java | 104 ++++++++++++++++++ .../arangodb/internal/ArangoSearchImpl.java | 2 +- .../internal/InternalArangoSearch.java | 6 +- 6 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 core/src/main/java/com/arangodb/ArangoSearchAsync.java create mode 100644 core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java diff --git a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java index 1b3523d59..72ddf89e1 100644 --- a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java @@ -305,11 +305,15 @@ public interface ArangoDatabaseAsync extends ArangoSerdeAccessor { */ ArangoViewAsync view(String name); -// /** -// * Asynchronous version of {@link ArangoDatabase#arangoSearch(String)} -// */ -// ArangoSearchAsync arangoSearch(String name); -// + /** + * Returns a {@link ArangoSearchAsync} instance for the given view name. + * + * @param name Name of the view + * @return ArangoSearch view handler + * @since ArangoDB 3.4.0 + */ + ArangoSearchAsync arangoSearch(String name); + // /** // * Asynchronous version of {@link ArangoDatabase#searchAlias(String)} // */ diff --git a/core/src/main/java/com/arangodb/ArangoSearchAsync.java b/core/src/main/java/com/arangodb/ArangoSearchAsync.java new file mode 100644 index 000000000..888b5e893 --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoSearchAsync.java @@ -0,0 +1,62 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.ViewEntity; +import com.arangodb.entity.arangosearch.ArangoSearchPropertiesEntity; +import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; +import com.arangodb.model.arangosearch.ArangoSearchPropertiesOptions; + +import javax.annotation.concurrent.ThreadSafe; +import java.util.concurrent.CompletableFuture; + +/** + * Asynchronous version of {@link ArangoSearch} + */ +@ThreadSafe +public interface ArangoSearchAsync extends ArangoViewAsync { + + /** + * Asynchronous version of {@link ArangoSearch#create()} + */ + CompletableFuture create(); + + /** + * Asynchronous version of {@link ArangoSearch#create(ArangoSearchCreateOptions)} + */ + CompletableFuture create(ArangoSearchCreateOptions options); + + /** + * Asynchronous version of {@link ArangoSearch#getProperties()} + */ + CompletableFuture getProperties(); + + /** + * Asynchronous version of {@link ArangoSearch#updateProperties(ArangoSearchPropertiesOptions)} + */ + CompletableFuture updateProperties(ArangoSearchPropertiesOptions options); + + /** + * Asynchronous version of {@link ArangoSearch#replaceProperties(ArangoSearchPropertiesOptions)} + */ + CompletableFuture replaceProperties(ArangoSearchPropertiesOptions options); + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index 85c5086b0..0e3b1d6ff 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -379,10 +379,10 @@ public ArangoViewAsync view(final String name) { return new ArangoViewAsyncImpl(this, name); } -// @Override -// public ArangoSearch arangoSearch(final String name) { -// return new ArangoSearchImpl(this, name); -// } + @Override + public ArangoSearchAsync arangoSearch(final String name) { + return new ArangoSearchAsyncImpl(this, name); + } // @Override // public SearchAlias searchAlias(String name) { diff --git a/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java new file mode 100644 index 000000000..70a540433 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java @@ -0,0 +1,104 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.*; +import com.arangodb.entity.ViewEntity; +import com.arangodb.entity.arangosearch.ArangoSearchPropertiesEntity; +import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; +import com.arangodb.model.arangosearch.ArangoSearchPropertiesOptions; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +/** + * @author Mark Vollmary + */ +public class ArangoSearchAsyncImpl extends InternalArangoSearch implements ArangoSearchAsync { + private final ArangoDatabaseAsync db; + + protected ArangoSearchAsyncImpl(final ArangoDatabaseAsyncImpl db, final String name) { + super(db, db.name(), name); + this.db = db; + } + + @Override + public ArangoDatabaseAsync db() { + return db; + } + + @Override + public CompletableFuture exists() { + return getInfo() + .thenApply(Objects::nonNull) + .exceptionally(e -> { + if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e.getCause(); + if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(aEx.getErrorNum())) { + return false; + } + } + throw new CompletionException(e); + }); + } + + @Override + public CompletableFuture drop() { + return executorAsync().execute(dropRequest(), Void.class); + } + + @Override + public CompletableFuture rename(final String newName) { + return executorAsync().execute(renameRequest(newName), ViewEntity.class); + } + + @Override + public CompletableFuture getInfo() { + return executorAsync().execute(getInfoRequest(), ViewEntity.class); + } + + @Override + public CompletableFuture create() { + return create(new ArangoSearchCreateOptions()); + } + + @Override + public CompletableFuture create(final ArangoSearchCreateOptions options) { + return db().createArangoSearch(name(), options); + } + + @Override + public CompletableFuture getProperties() { + return executorAsync().execute(getPropertiesRequest(), ArangoSearchPropertiesEntity.class); + } + + @Override + public CompletableFuture updateProperties(final ArangoSearchPropertiesOptions options) { + return executorAsync().execute(updatePropertiesRequest(options), ArangoSearchPropertiesEntity.class); + } + + @Override + public CompletableFuture replaceProperties(final ArangoSearchPropertiesOptions options) { + return executorAsync().execute(replacePropertiesRequest(options), ArangoSearchPropertiesEntity.class); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java b/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java index db616c303..e2f85a363 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoSearchImpl.java @@ -35,7 +35,7 @@ public class ArangoSearchImpl extends InternalArangoSearch implements ArangoSear private final ArangoDatabase db; protected ArangoSearchImpl(final ArangoDatabaseImpl db, final String name) { - super(db, name); + super(db, db.name(), name); this.db = db; } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java b/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java index a262d7232..753bf2b94 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoSearch.java @@ -30,9 +30,9 @@ public class InternalArangoSearch extends InternalArangoView { private static final String PROPERTIES_PATH = "properties"; private final String dbName; - protected InternalArangoSearch(final ArangoDatabaseImpl db, final String name) { - super(db, db.name(), name); - dbName = db.name(); + protected InternalArangoSearch(final ArangoExecuteable executeable, final String dbName, final String name) { + super(executeable, dbName, name); + this.dbName = dbName; } protected InternalRequest getPropertiesRequest() { From da501a231ef089c12c1d17371ee3e053bbc57ecc Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 17 Oct 2023 14:49:45 +0200 Subject: [PATCH 34/62] ArangoSearchAsyncTest --- .../com/arangodb/ArangoSearchAsyncTest.java | 1058 +++++++++++++++++ 1 file changed, 1058 insertions(+) create mode 100644 driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java diff --git a/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java new file mode 100644 index 000000000..d449582b1 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java @@ -0,0 +1,1058 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.ViewEntity; +import com.arangodb.entity.ViewType; +import com.arangodb.entity.arangosearch.*; +import com.arangodb.entity.arangosearch.analyzer.*; +import com.arangodb.model.arangosearch.AnalyzerDeleteOptions; +import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; +import com.arangodb.model.arangosearch.ArangoSearchPropertiesOptions; +import com.arangodb.model.arangosearch.SearchAliasCreateOptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.*; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoSearchAsyncTest extends BaseJunit5 { + + private static final String COLL_1 = "ArangoSearchTest_view_replace_prop"; + private static final String COLL_2 = "ArangoSearchTest_view_update_prop"; + + @BeforeAll + static void init() { + initCollections(COLL_1, COLL_2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void exists(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + db.createArangoSearch(viewName, new ArangoSearchCreateOptions()).get(); + assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createAndExistsSearchAlias(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = rndName(); + db.createSearchAlias(viewName, new SearchAliasCreateOptions()).get(); + assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getInfo(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + db.createArangoSearch(viewName, new ArangoSearchCreateOptions()).get(); + final ViewEntity info = db.arangoSearch(viewName).getInfo().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void drop(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + db.createArangoSearch(viewName, new ArangoSearchCreateOptions()).get(); + final ArangoSearchAsync view = db.arangoSearch(viewName); + view.drop().get(); + assertThat(view.exists().get()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void rename(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + final String name = viewName + "_new"; + db.createArangoSearch(name, new ArangoSearchCreateOptions()).get(); + db.arangoSearch(name).rename(viewName).get(); + assertThat(db.arangoSearch(name).exists().get()).isFalse(); + assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createArangoSearchView(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + final ViewEntity info = db.arangoSearch(viewName).create().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void createSearchAliasView(ArangoDatabaseAsync db) { +// assumeTrue(isAtLeastVersion(3, 10)); +// String viewName = rndName(); +// final ViewEntity info = db.searchAlias(viewName).create(); +// assertThat(info).isNotNull(); +// assertThat(info.getId()).isNotNull(); +// assertThat(info.getName()).isEqualTo(viewName); +// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); +// assertThat(db.searchAlias(viewName).exists()).isTrue(); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createArangoSearchViewWithOptions(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + final ArangoSearchCreateOptions options = new ArangoSearchCreateOptions(); + final ViewEntity info = db.arangoSearch(viewName).create(options).get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createArangoSearchViewWithPrimarySort(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + String viewName = rndName(); + final ArangoSearchCreateOptions options = new ArangoSearchCreateOptions(); + + final PrimarySort primarySort = PrimarySort.on("myFieldName"); + primarySort.ascending(true); + options.primarySort(primarySort); + options.primarySortCompression(ArangoSearchCompression.none); + options.consolidationIntervalMsec(666666L); + StoredValue storedValue = new StoredValue(Arrays.asList("a", "b"), ArangoSearchCompression.none); + options.storedValues(storedValue); + + final ArangoSearchAsync view = db.arangoSearch(viewName); + final ViewEntity info = view.create(options).get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); + + if (isAtLeastVersion(3, 7)) { + final ArangoSearchPropertiesEntity properties = view.getProperties().get(); + assertThat(properties.getPrimarySortCompression()).isEqualTo(ArangoSearchCompression.none); + Collection retrievedStoredValues = properties.getStoredValues(); + assertThat(retrievedStoredValues).isNotNull(); + assertThat(retrievedStoredValues).hasSize(1); + StoredValue retrievedStoredValue = retrievedStoredValues.iterator().next(); + assertThat(retrievedStoredValue).isNotNull(); + assertThat(retrievedStoredValue.getFields()).isEqualTo(storedValue.getFields()); + assertThat(retrievedStoredValue.getCompression()).isEqualTo(storedValue.getCompression()); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createArangoSearchViewWithCommitIntervalMsec(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + String viewName = rndName(); + final ArangoSearchCreateOptions options = new ArangoSearchCreateOptions(); + options.commitIntervalMsec(666666L); + + final ViewEntity info = db.arangoSearch(viewName).create(options).get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); + + // check commit interval msec property + final ArangoSearchAsync view = db.arangoSearch(viewName); + final ArangoSearchPropertiesEntity properties = view.getProperties().get(); + assertThat(properties.getCommitIntervalMsec()).isEqualTo(666666L); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void createSearchAliasViewWithOptions(ArangoDatabaseAsync db) { +// assumeTrue(isAtLeastVersion(3, 10)); +// String viewName = rndName(); +// final SearchAliasCreateOptions options = new SearchAliasCreateOptions(); +// final ViewEntity info = db.searchAlias(viewName).create(options); +// assertThat(info).isNotNull(); +// assertThat(info.getId()).isNotNull(); +// assertThat(info.getName()).isEqualTo(viewName); +// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); +// assertThat(db.searchAlias(viewName).exists()).isTrue(); +// } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void createSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) { +// assumeTrue(isAtLeastVersion(3, 10)); +// ArangoCollectionAsync col = db.collection(COLL_1); +// String idxName1 = rndName(); +// col.ensureInvertedIndex(new InvertedIndexOptions() +// .name(idxName1) +// .fields(new InvertedIndexField().name("a" + rnd()))); +// +// String idxName2 = rndName(); +// col.ensureInvertedIndex(new InvertedIndexOptions() +// .name(idxName2) +// .fields(new InvertedIndexField().name("a" + rnd()))); +// +// String viewName = rndName(); +// final SearchAliasCreateOptions options = new SearchAliasCreateOptions() +// .indexes( +// new SearchAliasIndex(COLL_1, idxName1, SearchAliasIndex.OperationType.add), +// new SearchAliasIndex(COLL_1, idxName2, SearchAliasIndex.OperationType.add), +// new SearchAliasIndex(COLL_1, idxName2, SearchAliasIndex.OperationType.del) +// ); +// final ViewEntity info = db.searchAlias(viewName).create(options); +// assertThat(info).isNotNull(); +// assertThat(info.getId()).isNotNull(); +// assertThat(info.getName()).isEqualTo(viewName); +// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); +// +// final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties(); +// assertThat(properties).isNotNull(); +// assertThat(properties.getId()).isNotNull(); +// assertThat(properties.getName()).isEqualTo(viewName); +// assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); +// assertThat(properties.getIndexes()) +// .isNotNull() +// .isNotEmpty() +// .anyMatch(i -> i.getCollection().equals(COLL_1) && i.getIndex().equals(idxName1)); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getArangoSearchViewProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + final ArangoSearchAsync view = db.arangoSearch(viewName); + view.create(new ArangoSearchCreateOptions()).get(); + final ArangoSearchPropertiesEntity properties = view.getProperties().get(); + assertThat(properties).isNotNull(); + assertThat(properties.getId()).isNotNull(); + assertThat(properties.getName()).isEqualTo(viewName); + assertThat(properties.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(properties.getConsolidationIntervalMsec()).isNotNull(); + assertThat(properties.getCleanupIntervalStep()).isNotNull(); + final ConsolidationPolicy consolidate = properties.getConsolidationPolicy(); + assertThat(consolidate).isNotNull(); + final Collection links = properties.getLinks(); + assertThat(links).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void updateArangoSearchViewProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + final ArangoSearchAsync view = db.arangoSearch(viewName); + view.create(new ArangoSearchCreateOptions()).get(); + final ArangoSearchPropertiesOptions options = new ArangoSearchPropertiesOptions(); + options.cleanupIntervalStep(15L); + options.consolidationIntervalMsec(65000L); + options.consolidationPolicy(ConsolidationPolicy.of(ConsolidationType.BYTES_ACCUM).threshold(1.)); + options.link(CollectionLink.on(COLL_2) + .fields(FieldLink.on("value").analyzers("identity").trackListPositions(true).includeAllFields(true) + .storeValues(StoreValuesType.ID))); + final ArangoSearchPropertiesEntity properties = view.updateProperties(options).get(); + assertThat(properties).isNotNull(); + assertThat(properties.getCleanupIntervalStep()).isEqualTo(15L); + assertThat(properties.getConsolidationIntervalMsec()).isEqualTo(65000L); + final ConsolidationPolicy consolidate = properties.getConsolidationPolicy(); + assertThat(consolidate).isNotNull(); + assertThat(consolidate.getType()).isEqualTo(ConsolidationType.BYTES_ACCUM); + assertThat(consolidate.getThreshold()).isEqualTo(1.); + assertThat(properties.getLinks()).hasSize(1); + final CollectionLink link = properties.getLinks().iterator().next(); + assertThat(link.getName()).isEqualTo(COLL_2); + assertThat(link.getFields()).hasSize(1); + final FieldLink next = link.getFields().iterator().next(); + assertThat(next.getName()).isEqualTo("value"); + assertThat(next.getIncludeAllFields()).isTrue(); + assertThat(next.getTrackListPositions()).isTrue(); + assertThat(next.getStoreValues()).isEqualTo(StoreValuesType.ID); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void updateSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { +// assumeTrue(isAtLeastVersion(3, 10)); +// ArangoCollectionAsync col = db.collection(COLL_1); +// String idxName = rndName(); +// col.ensureInvertedIndex(new InvertedIndexOptions() +// .name(idxName) +// .fields(new InvertedIndexField().name("a" + rnd()))).get(); +// ArangoCollectionAsync col2 = db.collection(COLL_2); +// String idxName2 = rndName(); +// col2.ensureInvertedIndex(new InvertedIndexOptions() +// .name(idxName2) +// .fields(new InvertedIndexField().name("a" + rnd()))).get(); +// +// String viewName = rndName(); +// final SearchAliasCreateOptions options = new SearchAliasCreateOptions() +// .indexes(new SearchAliasIndex(COLL_1, idxName)); +// final ViewEntity info = db.searchAlias(viewName).create(options); +// db.searchAlias(viewName).updateProperties(new SearchAliasPropertiesOptions() +// .indexes(new SearchAliasIndex(COLL_2, idxName2))); +// +// assertThat(info).isNotNull(); +// assertThat(info.getId()).isNotNull(); +// assertThat(info.getName()).isEqualTo(viewName); +// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); +// +// final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties(); +// assertThat(properties).isNotNull(); +// assertThat(properties.getId()).isNotNull(); +// assertThat(properties.getName()).isEqualTo(viewName); +// assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); +// assertThat(properties.getIndexes()) +// .isNotNull() +// .isNotEmpty() +// .hasSize(2) +// .anyMatch(i -> i.getCollection().equals(COLL_1) && i.getIndex().equals(idxName)) +// .anyMatch(i -> i.getCollection().equals(COLL_2) && i.getIndex().equals(idxName2)); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void replaceArangoSearchViewProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + final ArangoSearchAsync view = db.arangoSearch(viewName); + view.create(new ArangoSearchCreateOptions()).get(); + final ArangoSearchPropertiesOptions options = new ArangoSearchPropertiesOptions(); + options.link(CollectionLink.on(COLL_1) + .fields(FieldLink.on("value").analyzers("identity"))); + final ArangoSearchPropertiesEntity properties = view.replaceProperties(options).get(); + assertThat(properties).isNotNull(); + assertThat(properties.getLinks()).hasSize(1); + final CollectionLink link = properties.getLinks().iterator().next(); + assertThat(link.getName()).isEqualTo(COLL_1); + assertThat(link.getFields()).hasSize(1); + assertThat(link.getFields().iterator().next().getName()).isEqualTo("value"); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void replaceSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { +// assumeTrue(isAtLeastVersion(3, 10)); +// ArangoCollectionAsync col = db.collection(COLL_1); +// String idxName = rndName(); +// col.ensureInvertedIndex(new InvertedIndexOptions() +// .name(idxName) +// .fields(new InvertedIndexField().name("a" + rnd()))).get(); +// ArangoCollectionAsync col2 = db.collection(COLL_2); +// String idxName2 = rndName(); +// col2.ensureInvertedIndex(new InvertedIndexOptions() +// .name(idxName2) +// .fields(new InvertedIndexField().name("a" + rnd()))).get(); +// +// String viewName = rndName(); +// final SearchAliasCreateOptions options = new SearchAliasCreateOptions() +// .indexes(new SearchAliasIndex(COLL_1, idxName)); +// final ViewEntity info = db.searchAlias(viewName).create(options); +// db.searchAlias(viewName).replaceProperties(new SearchAliasPropertiesOptions() +// .indexes(new SearchAliasIndex(COLL_2, idxName2))); +// +// assertThat(info).isNotNull(); +// assertThat(info.getId()).isNotNull(); +// assertThat(info.getName()).isEqualTo(viewName); +// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); +// +// final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties(); +// assertThat(properties).isNotNull(); +// assertThat(properties.getId()).isNotNull(); +// assertThat(properties.getName()).isEqualTo(viewName); +// assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); +// assertThat(properties.getIndexes()) +// .isNotNull() +// .isNotEmpty() +// .hasSize(1) +// .anyMatch(i -> i.getCollection().equals(COLL_2) && i.getIndex().equals(idxName2)); +// } + + private void createGetAndDeleteTypedAnalyzer(ArangoDatabaseAsync db, SearchAnalyzer analyzer) throws ExecutionException, InterruptedException { + + String fullyQualifiedName = db.name() + "::" + analyzer.getName(); + analyzer.setName(fullyQualifiedName); + + // createAnalyzer + SearchAnalyzer createdAnalyzer = db.createSearchAnalyzer(analyzer).get(); + assertThat(createdAnalyzer).isEqualTo(analyzer); + + // getAnalyzer + SearchAnalyzer gotAnalyzer = db.getSearchAnalyzer(analyzer.getName()).get(); + assertThat(gotAnalyzer).isEqualTo(analyzer); + + // getAnalyzers + SearchAnalyzer foundAnalyzer = + db.getSearchAnalyzers().get().stream().filter(it -> it.getName().equals(fullyQualifiedName)) + .findFirst().get(); + assertThat(foundAnalyzer).isEqualTo(analyzer); + + // deleteAnalyzer + AnalyzerDeleteOptions deleteOptions = new AnalyzerDeleteOptions(); + deleteOptions.setForce(true); + + db.deleteSearchAnalyzer(analyzer.getName(), deleteOptions).get(); + + Throwable thrown = catchThrowable(() -> db.getSearchAnalyzer(analyzer.getName()).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(404); + assertThat(e.getErrorNum()).isEqualTo(1202); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void identityAnalyzerTyped(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + IdentityAnalyzer analyzer = new IdentityAnalyzer(); + analyzer.setFeatures(features); + analyzer.setName(name); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void delimiterAnalyzerTyped(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + DelimiterAnalyzerProperties properties = new DelimiterAnalyzerProperties(); + properties.setDelimiter("-"); + + DelimiterAnalyzer analyzer = new DelimiterAnalyzer(); + analyzer.setFeatures(features); + analyzer.setName(name); + analyzer.setProperties(properties); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void stemAnalyzerTyped(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + StemAnalyzerProperties properties = new StemAnalyzerProperties(); + properties.setLocale("ru"); + + StemAnalyzer options = new StemAnalyzer(); + options.setFeatures(features); + options.setName(name); + options.setProperties(properties); + + createGetAndDeleteTypedAnalyzer(db, options); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void normAnalyzerTyped(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + NormAnalyzerProperties properties = new NormAnalyzerProperties(); + properties.setLocale("ru"); + properties.setAnalyzerCase(SearchAnalyzerCase.lower); + properties.setAccent(true); + + NormAnalyzer options = new NormAnalyzer(); + options.setFeatures(features); + options.setName(name); + options.setProperties(properties); + + createGetAndDeleteTypedAnalyzer(db, options); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void ngramAnalyzerTyped(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + NGramAnalyzerProperties properties = new NGramAnalyzerProperties(); + properties.setMax(6L); + properties.setMin(3L); + properties.setPreserveOriginal(true); + + NGramAnalyzer analyzer = new NGramAnalyzer(); + analyzer.setFeatures(features); + analyzer.setName(name); + analyzer.setType(AnalyzerType.ngram); + analyzer.setProperties(properties); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void enhancedNgramAnalyzerTyped(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 6)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + NGramAnalyzerProperties properties = new NGramAnalyzerProperties(); + properties.setMax(6L); + properties.setMin(3L); + properties.setPreserveOriginal(true); + properties.setStartMarker("^"); + properties.setEndMarker("^"); + properties.setStreamType(StreamType.utf8); + + NGramAnalyzer analyzer = new NGramAnalyzer(); + analyzer.setFeatures(features); + analyzer.setName(name); + analyzer.setProperties(properties); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void textAnalyzerTyped(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + TextAnalyzerProperties properties = new TextAnalyzerProperties(); + properties.setLocale("ru"); + properties.setAnalyzerCase(SearchAnalyzerCase.lower); + properties.setAccent(true); + properties.setStemming(true); + + TextAnalyzer analyzer = new TextAnalyzer(); + analyzer.setFeatures(features); + analyzer.setName(name); + analyzer.setType(AnalyzerType.text); + analyzer.setProperties(properties); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void enhancedTextAnalyzerTyped(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 6)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + EdgeNgram edgeNgram = new EdgeNgram(); + edgeNgram.setMin(2L); + edgeNgram.setMax(100000L); + edgeNgram.setPreserveOriginal(true); + + TextAnalyzerProperties properties = new TextAnalyzerProperties(); + properties.setLocale("ru"); + properties.setAnalyzerCase(SearchAnalyzerCase.lower); + properties.setAccent(true); + properties.setStemming(true); + properties.setEdgeNgram(edgeNgram); + + TextAnalyzer analyzer = new TextAnalyzer(); + analyzer.setFeatures(features); + analyzer.setName(name); + analyzer.setProperties(properties); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void arangoSearchOptions(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = rndName(); + FieldLink field = FieldLink.on("f1") + .inBackground(true) + .cache(false); + if (isEnterprise()) { + field.nested(FieldLink.on("f2")); + } + CollectionLink link = CollectionLink.on(COLL_1) + .analyzers("identity") + .fields(field) + .includeAllFields(true) + .storeValues(StoreValuesType.ID) + .trackListPositions(false) + .inBackground(true) + .cache(true); + + if (isEnterprise()) { + link.nested(FieldLink.on("f3")); + } + ArangoSearchCreateOptions options = new ArangoSearchCreateOptions() + .link(link) + .primarySortCache(true) + .primaryKeyCache(true); + StoredValue storedValue = new StoredValue(Arrays.asList("a", "b"), ArangoSearchCompression.none, true); + options.storedValues(storedValue); + String[] optimizeTopK = new String[]{"BM25(@doc) DESC", "TFIDF(@doc) DESC"}; + options.optimizeTopK(optimizeTopK); + + final ArangoSearchAsync view = db.arangoSearch(viewName); + view.create(options).get(); + + final ArangoSearchPropertiesEntity properties = view.getProperties().get(); + assertThat(properties).isNotNull(); + assertThat(properties.getId()).isNotNull(); + assertThat(properties.getName()).isEqualTo(viewName); + assertThat(properties.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(properties.getLinks()).isNotEmpty(); + + CollectionLink createdLink = properties.getLinks().iterator().next(); + assertThat(createdLink.getName()).isEqualTo(COLL_1); + assertThat(createdLink.getAnalyzers()).contains("identity"); + assertThat(createdLink.getIncludeAllFields()).isTrue(); + assertThat(createdLink.getStoreValues()).isEqualTo(StoreValuesType.ID); + assertThat(createdLink.getTrackListPositions()).isFalse(); + + FieldLink fieldLink = createdLink.getFields().iterator().next(); + if (isEnterprise()) { + assertThat(createdLink.getCache()).isTrue(); + assertThat(fieldLink.getCache()).isFalse(); + assertThat(properties.getPrimaryKeyCache()).isTrue(); + assertThat(properties.getPrimarySortCache()).isTrue(); + assertThat(properties.getStoredValues()) + .isNotEmpty() + .allSatisfy(it -> assertThat(it.getCache()).isTrue()); + } + + if (isEnterprise() && isAtLeastVersion(3, 10)) { + assertThat(createdLink.getNested()).isNotEmpty(); + FieldLink nested = createdLink.getNested().iterator().next(); + assertThat(nested.getName()).isEqualTo("f3"); + } + + assertThat(fieldLink.getName()).isEqualTo("f1"); + if (isEnterprise() && isAtLeastVersion(3, 10)) { + assertThat(fieldLink.getNested()).isNotEmpty(); + FieldLink nested = fieldLink.getNested().iterator().next(); + assertThat(nested.getName()).isEqualTo("f2"); + } + + if (isEnterprise() && isAtLeastVersion(3, 11)) { + // FIXME: BTS-1428 + // assertThat(properties.getOptimizeTopK()).containsExactly(optimizeTopK); + } + + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void pipelineAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + + // comma delimiter + DelimiterAnalyzerProperties commaDelimiterProperties = new DelimiterAnalyzerProperties(); + commaDelimiterProperties.setDelimiter(","); + + DelimiterAnalyzer commaDelimiter = new DelimiterAnalyzer(); + commaDelimiter.setProperties(commaDelimiterProperties); + + // semicolon delimiter + DelimiterAnalyzerProperties semicolonDelimiterProperties = new DelimiterAnalyzerProperties(); + semicolonDelimiterProperties.setDelimiter(","); + + DelimiterAnalyzer semicolonDelimiter = new DelimiterAnalyzer(); + semicolonDelimiter.setProperties(semicolonDelimiterProperties); + + // stem + StemAnalyzerProperties stemAnalyzerProperties = new StemAnalyzerProperties(); + stemAnalyzerProperties.setLocale("en"); + + StemAnalyzer stemAnalyzer = new StemAnalyzer(); + stemAnalyzer.setProperties(stemAnalyzerProperties); + + // pipeline analyzer + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + PipelineAnalyzerProperties properties = new PipelineAnalyzerProperties() + .addAnalyzer(commaDelimiter) + .addAnalyzer(semicolonDelimiter) + .addAnalyzer(stemAnalyzer); + + PipelineAnalyzer pipelineAnalyzer = new PipelineAnalyzer(); + pipelineAnalyzer.setName("test-" + UUID.randomUUID()); + pipelineAnalyzer.setProperties(properties); + pipelineAnalyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, pipelineAnalyzer); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void stopwordsAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { +// assumeTrue(isAtLeastVersion(3, 8)); +// +// Set features = new HashSet<>(); +// features.add(AnalyzerFeature.frequency); +// features.add(AnalyzerFeature.norm); +// features.add(AnalyzerFeature.position); +// +// StopwordsAnalyzerProperties properties = new StopwordsAnalyzerProperties() +// .addStopwordAsHex("616e64") +// .addStopwordAsString("the"); +// +// assertThat(properties.getStopwordsAsStringList()).contains("and"); +// assertThat(properties.getStopwordsAsHexList()).contains("746865"); +// +// StopwordsAnalyzer analyzer = new StopwordsAnalyzer(); +// String name = "test-" + UUID.randomUUID(); +// analyzer.setName(name); +// analyzer.setProperties(properties); +// analyzer.setFeatures(features); +// +// createGetAndDeleteTypedAnalyzer(db, analyzer); +// db.createSearchAnalyzer(analyzer); +// Collection res = db.query("RETURN FLATTEN(TOKENS(SPLIT('the fox and the dog and a theater', ' '), " + +// "@aName))", Collection.class, +// Collections.singletonMap("aName", name)).next(); +// assertThat(res).containsExactly("fox", "dog", "a", "theater"); +// db.deleteSearchAnalyzer(name); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void aqlAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + + AQLAnalyzerProperties properties = new AQLAnalyzerProperties(); + properties.setBatchSize(2); + properties.setCollapsePositions(true); + properties.setKeepNull(false); + properties.setMemoryLimit(2200L); + properties.setQueryString("RETURN SOUNDEX(@param)"); + properties.setReturnType(AQLAnalyzerProperties.ReturnType.string); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + AQLAnalyzer aqlAnalyzer = new AQLAnalyzer(); + aqlAnalyzer.setName("test-" + UUID.randomUUID()); + aqlAnalyzer.setProperties(properties); + aqlAnalyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, aqlAnalyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void geoJsonAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + + GeoAnalyzerOptions options = new GeoAnalyzerOptions(); + options.setMaxLevel(10); + options.setMaxCells(11); + options.setMinLevel(8); + + GeoJSONAnalyzerProperties properties = new GeoJSONAnalyzerProperties(); + properties.setOptions(options); + properties.setType(GeoJSONAnalyzerProperties.GeoJSONAnalyzerType.point); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + GeoJSONAnalyzer geoJSONAnalyzer = new GeoJSONAnalyzer(); + geoJSONAnalyzer.setName("test-" + UUID.randomUUID()); + geoJSONAnalyzer.setProperties(properties); + geoJSONAnalyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, geoJSONAnalyzer); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void geoS2Analyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 10, 5)); + + GeoAnalyzerOptions options = new GeoAnalyzerOptions(); + options.setMaxLevel(10); + options.setMaxCells(11); + options.setMinLevel(8); + + GeoS2AnalyzerProperties properties = new GeoS2AnalyzerProperties(); + properties.setOptions(options); + properties.setType(GeoS2AnalyzerProperties.GeoS2AnalyzerType.point); + properties.setFormat(GeoS2AnalyzerProperties.GeoS2Format.s2Point); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + GeoS2Analyzer geoS2Analyzer = new GeoS2Analyzer(); + geoS2Analyzer.setName("test-" + UUID.randomUUID()); + geoS2Analyzer.setProperties(properties); + geoS2Analyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, geoS2Analyzer); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void geoPointAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + + GeoAnalyzerOptions options = new GeoAnalyzerOptions(); + options.setMaxLevel(10); + options.setMaxCells(11); + options.setMinLevel(8); + + GeoPointAnalyzerProperties properties = new GeoPointAnalyzerProperties(); + properties.setLatitude(new String[]{"a", "b", "c"}); + properties.setLongitude(new String[]{"d", "e", "f"}); + properties.setOptions(options); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + GeoPointAnalyzer geoPointAnalyzer = new GeoPointAnalyzer(); + geoPointAnalyzer.setName("test-" + UUID.randomUUID()); + geoPointAnalyzer.setProperties(properties); + geoPointAnalyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, geoPointAnalyzer); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void segmentationAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 9)); + + SegmentationAnalyzerProperties properties = new SegmentationAnalyzerProperties(); + properties.setBreakMode(SegmentationAnalyzerProperties.BreakMode.graphic); + properties.setAnalyzerCase(SearchAnalyzerCase.upper); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + SegmentationAnalyzer segmentationAnalyzer = new SegmentationAnalyzer(); + segmentationAnalyzer.setName("test-" + UUID.randomUUID()); + segmentationAnalyzer.setProperties(properties); + segmentationAnalyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, segmentationAnalyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void collationAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 9)); + + CollationAnalyzerProperties properties = new CollationAnalyzerProperties(); + properties.setLocale("ru"); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + CollationAnalyzer collationAnalyzer = new CollationAnalyzer(); + collationAnalyzer.setName("test-" + UUID.randomUUID()); + collationAnalyzer.setProperties(properties); + collationAnalyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, collationAnalyzer); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void classificationAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + assumeTrue(isEnterprise()); + + ClassificationAnalyzerProperties properties = new ClassificationAnalyzerProperties(); + properties.setModelLocation("/tmp/foo.bin"); + properties.setTopK(2); + properties.setThreshold(.5); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + ClassificationAnalyzer analyzer = new ClassificationAnalyzer(); + analyzer.setName("test-" + UUID.randomUUID()); + analyzer.setProperties(properties); + analyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void nearestNeighborsAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + assumeTrue(isEnterprise()); + + NearestNeighborsAnalyzerProperties properties = new NearestNeighborsAnalyzerProperties(); + properties.setModelLocation("/tmp/foo.bin"); + properties.setTopK(2); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + NearestNeighborsAnalyzer analyzer = new NearestNeighborsAnalyzer(); + analyzer.setName("test-" + UUID.randomUUID()); + analyzer.setProperties(properties); + analyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void MinHashAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + assumeTrue(isEnterprise()); + + SegmentationAnalyzerProperties segProperties = new SegmentationAnalyzerProperties(); + segProperties.setBreakMode(SegmentationAnalyzerProperties.BreakMode.alpha); + segProperties.setAnalyzerCase(SearchAnalyzerCase.lower); + + SegmentationAnalyzer segAnalyzer = new SegmentationAnalyzer(); + segAnalyzer.setProperties(segProperties); + + MinHashAnalyzerProperties properties = new MinHashAnalyzerProperties(); + properties.setAnalyzer(segAnalyzer); + properties.setNumHashes(2); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + MinHashAnalyzer analyzer = new MinHashAnalyzer(); + analyzer.setName("test-" + UUID.randomUUID()); + analyzer.setProperties(properties); + analyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void offsetFeature(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + String name = "test-" + UUID.randomUUID(); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + features.add(AnalyzerFeature.offset); + + IdentityAnalyzer analyzer = new IdentityAnalyzer(); + analyzer.setFeatures(features); + analyzer.setName(name); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + } + +} From 6b5520fc71266a5159ba071e61730692f5ca6b6e Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 17 Oct 2023 15:12:48 +0200 Subject: [PATCH 35/62] SearchAliasAsync --- .../com/arangodb/ArangoDatabaseAsync.java | 12 +- .../java/com/arangodb/SearchAliasAsync.java | 60 ++++ .../internal/ArangoDatabaseAsyncImpl.java | 8 +- .../internal/InternalSearchAlias.java | 6 +- .../internal/SearchAliasAsyncImpl.java | 104 +++++++ .../arangodb/internal/SearchAliasImpl.java | 2 +- .../com/arangodb/ArangoDatabaseAsyncTest.java | 64 ++-- .../com/arangodb/ArangoSearchAsyncTest.java | 287 +++++++++--------- .../ArangoVertexCollectionAsyncTest.java | 2 +- .../com/arangodb/ArangoViewAsyncTest.java | 2 +- 10 files changed, 357 insertions(+), 190 deletions(-) create mode 100644 core/src/main/java/com/arangodb/SearchAliasAsync.java create mode 100644 core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java diff --git a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java index 72ddf89e1..8b6f22052 100644 --- a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java @@ -314,10 +314,14 @@ public interface ArangoDatabaseAsync extends ArangoSerdeAccessor { */ ArangoSearchAsync arangoSearch(String name); -// /** -// * Asynchronous version of {@link ArangoDatabase#searchAlias(String)} -// */ -// SearchAliasAsync searchAlias(String name); + /** + * Returns a {@link SearchAliasAsync} instance for the given view name. + * + * @param name Name of the view + * @return SearchAlias view handler + * @since ArangoDB 3.10 + */ + SearchAliasAsync searchAlias(String name); /** * Asynchronous version of {@link ArangoDatabase#createView(String, ViewType)} diff --git a/core/src/main/java/com/arangodb/SearchAliasAsync.java b/core/src/main/java/com/arangodb/SearchAliasAsync.java new file mode 100644 index 000000000..22ea9280c --- /dev/null +++ b/core/src/main/java/com/arangodb/SearchAliasAsync.java @@ -0,0 +1,60 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.ViewEntity; +import com.arangodb.entity.arangosearch.SearchAliasPropertiesEntity; +import com.arangodb.model.arangosearch.SearchAliasCreateOptions; +import com.arangodb.model.arangosearch.SearchAliasPropertiesOptions; + +import java.util.concurrent.CompletableFuture; + +/** + * Asynchronous version of {@link SearchAlias} + */ +public interface SearchAliasAsync extends ArangoViewAsync { + + /** + * Asynchronous version of {@link SearchAlias#create()} + */ + CompletableFuture create(); + + /** + * Asynchronous version of {@link SearchAlias#create(SearchAliasCreateOptions)} + */ + CompletableFuture create(SearchAliasCreateOptions options); + + /** + * Asynchronous version of {@link SearchAlias#getProperties()} + */ + CompletableFuture getProperties(); + + /** + * Asynchronous version of {@link SearchAlias#updateProperties(SearchAliasPropertiesOptions)} + */ + CompletableFuture updateProperties(SearchAliasPropertiesOptions options); + + /** + * Asynchronous version of {@link SearchAlias#replaceProperties(SearchAliasPropertiesOptions)} + */ + CompletableFuture replaceProperties(SearchAliasPropertiesOptions options); + +} diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index 0e3b1d6ff..94c111a93 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -384,10 +384,10 @@ public ArangoSearchAsync arangoSearch(final String name) { return new ArangoSearchAsyncImpl(this, name); } -// @Override -// public SearchAlias searchAlias(String name) { -// return new SearchAliasImpl(this, name); -// } + @Override + public SearchAliasAsync searchAlias(String name) { + return new SearchAliasAsyncImpl(this, name); + } @Override public CompletableFuture createView(final String name, final ViewType type) { diff --git a/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java b/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java index da540426a..81892a7c7 100644 --- a/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java +++ b/core/src/main/java/com/arangodb/internal/InternalSearchAlias.java @@ -27,9 +27,9 @@ public class InternalSearchAlias extends InternalArangoView { private static final String PROPERTIES_PATH = "properties"; private final String dbName; - protected InternalSearchAlias(final ArangoDatabaseImpl db, final String name) { - super(db, db.name(), name); - dbName = db.name(); + protected InternalSearchAlias(final ArangoExecuteable executeable, final String dbName, final String name) { + super(executeable, dbName, name); + this.dbName = dbName; } protected InternalRequest getPropertiesRequest() { diff --git a/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java b/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java new file mode 100644 index 000000000..721edb5d9 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java @@ -0,0 +1,104 @@ +/* + * DISCLAIMER + * + * Copyright 2018 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.*; +import com.arangodb.entity.ViewEntity; +import com.arangodb.entity.arangosearch.SearchAliasPropertiesEntity; +import com.arangodb.model.arangosearch.SearchAliasCreateOptions; +import com.arangodb.model.arangosearch.SearchAliasPropertiesOptions; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +/** + * @author Michele Rastelli + */ +public class SearchAliasAsyncImpl extends InternalSearchAlias implements SearchAliasAsync { + private final ArangoDatabaseAsync db; + + protected SearchAliasAsyncImpl(final ArangoDatabaseAsyncImpl db, final String name) { + super(db, db.name(), name); + this.db = db; + } + + @Override + public ArangoDatabaseAsync db() { + return db; + } + + @Override + public CompletableFuture exists() { + return getInfo() + .thenApply(Objects::nonNull) + .exceptionally(e -> { + if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e.getCause(); + if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(aEx.getErrorNum())) { + return false; + } + } + throw new CompletionException(e); + }); + } + + @Override + public CompletableFuture drop() { + return executorAsync().execute(dropRequest(), Void.class); + } + + @Override + public CompletableFuture rename(final String newName) { + return executorAsync().execute(renameRequest(newName), ViewEntity.class); + } + + @Override + public CompletableFuture getInfo() { + return executorAsync().execute(getInfoRequest(), ViewEntity.class); + } + + @Override + public CompletableFuture create() { + return create(new SearchAliasCreateOptions()); + } + + @Override + public CompletableFuture create(final SearchAliasCreateOptions options) { + return db().createSearchAlias(name(), options); + } + + @Override + public CompletableFuture getProperties() { + return executorAsync().execute(getPropertiesRequest(), SearchAliasPropertiesEntity.class); + } + + @Override + public CompletableFuture updateProperties(final SearchAliasPropertiesOptions options) { + return executorAsync().execute(updatePropertiesRequest(options), SearchAliasPropertiesEntity.class); + } + + @Override + public CompletableFuture replaceProperties(final SearchAliasPropertiesOptions options) { + return executorAsync().execute(replacePropertiesRequest(options), SearchAliasPropertiesEntity.class); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java b/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java index 364efdcc1..b7bc07a9e 100644 --- a/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java +++ b/core/src/main/java/com/arangodb/internal/SearchAliasImpl.java @@ -34,7 +34,7 @@ public class SearchAliasImpl extends InternalSearchAlias implements SearchAlias { private final ArangoDatabase db; protected SearchAliasImpl(final ArangoDatabaseImpl db, final String name) { - super(db, name); + super(db, db.name(), name); this.db = db; } diff --git a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java index a2f869cab..bbf8857dd 100644 --- a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java @@ -1082,33 +1082,33 @@ void explainQueryWithBindVars(ArangoDatabaseAsync db) throws ExecutionException, assertThat(plan.getNodes()).isNotEmpty(); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void explainQueryWithIndexNode(ArangoDatabaseAsync db) { -// ArangoCollection character = db.collection("got_characters"); -// ArangoCollection actor = db.collection("got_actors"); -// -// if (!character.exists()) -// character.create(); -// -// if (!actor.exists()) -// actor.create(); -// -// String query = "" + -// "FOR `character` IN `got_characters` " + -// " FOR `actor` IN `got_actors` " + -// " FILTER `character`.`actor` == `actor`.`_id` " + -// " RETURN `character`"; -// -// final ExecutionPlan plan = db.explainQuery(query, null, null).getPlan(); -// plan.getNodes().stream() -// .filter(it -> "IndexNode".equals(it.getType())) -// .flatMap(it -> it.getIndexes().stream()) -// .forEach(it -> { -// assertThat(it.getType()).isEqualTo(IndexType.primary); -// assertThat(it.getFields()).contains("_key"); -// }); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void explainQueryWithIndexNode(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + ArangoCollectionAsync character = db.collection("got_characters"); + ArangoCollectionAsync actor = db.collection("got_actors"); + + if (!character.exists().get()) + character.create().get(); + + if (!actor.exists().get()) + actor.create().get(); + + String query = "" + + "FOR `character` IN `got_characters` " + + " FOR `actor` IN `got_actors` " + + " FILTER `character`.`actor` == `actor`.`_id` " + + " RETURN `character`"; + + final ExecutionPlan plan = db.explainQuery(query, null, null).get().getPlan(); + plan.getNodes().stream() + .filter(it -> "IndexNode".equals(it.getType())) + .flatMap(it -> it.getIndexes().stream()) + .forEach(it -> { + assertThat(it.getType()).isEqualTo(IndexType.primary); + assertThat(it.getFields()).contains("_key"); + }); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -1277,11 +1277,11 @@ void createGraphSatellite(ArangoDatabaseAsync db) throws ExecutionException, Int final GraphEntity result = db.createGraph(name, null, new GraphCreateOptions().replicationFactor(ReplicationFactor.ofSatellite())).get(); assertThat(result.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); -// GraphEntity info = db.graph(name).getInfo(); -// assertThat(info.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); -// -// GraphEntity graph = db.getGraphs().stream().filter(g -> name.equals(g.getName())).findFirst().get(); -// assertThat(graph.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + GraphEntity info = db.graph(name).getInfo().get(); + assertThat(info.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + + GraphEntity graph = db.getGraphs().get().stream().filter(g -> name.equals(g.getName())).findFirst().get(); + assertThat(graph.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); } @ParameterizedTest(name = "{index}") diff --git a/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java index d449582b1..7713ae878 100644 --- a/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java @@ -20,14 +20,13 @@ package com.arangodb; +import com.arangodb.entity.InvertedIndexField; import com.arangodb.entity.ViewEntity; import com.arangodb.entity.ViewType; import com.arangodb.entity.arangosearch.*; import com.arangodb.entity.arangosearch.analyzer.*; -import com.arangodb.model.arangosearch.AnalyzerDeleteOptions; -import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; -import com.arangodb.model.arangosearch.ArangoSearchPropertiesOptions; -import com.arangodb.model.arangosearch.SearchAliasCreateOptions; +import com.arangodb.model.InvertedIndexOptions; +import com.arangodb.model.arangosearch.*; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -121,18 +120,18 @@ void createArangoSearchView(ArangoDatabaseAsync db) throws ExecutionException, I assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void createSearchAliasView(ArangoDatabaseAsync db) { -// assumeTrue(isAtLeastVersion(3, 10)); -// String viewName = rndName(); -// final ViewEntity info = db.searchAlias(viewName).create(); -// assertThat(info).isNotNull(); -// assertThat(info.getId()).isNotNull(); -// assertThat(info.getName()).isEqualTo(viewName); -// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); -// assertThat(db.searchAlias(viewName).exists()).isTrue(); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createSearchAliasView(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = rndName(); + final ViewEntity info = db.searchAlias(viewName).create().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + assertThat(db.searchAlias(viewName).exists().get()).isTrue(); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -205,58 +204,58 @@ void createArangoSearchViewWithCommitIntervalMsec(ArangoDatabaseAsync db) throws assertThat(properties.getCommitIntervalMsec()).isEqualTo(666666L); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void createSearchAliasViewWithOptions(ArangoDatabaseAsync db) { -// assumeTrue(isAtLeastVersion(3, 10)); -// String viewName = rndName(); -// final SearchAliasCreateOptions options = new SearchAliasCreateOptions(); -// final ViewEntity info = db.searchAlias(viewName).create(options); -// assertThat(info).isNotNull(); -// assertThat(info.getId()).isNotNull(); -// assertThat(info.getName()).isEqualTo(viewName); -// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); -// assertThat(db.searchAlias(viewName).exists()).isTrue(); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createSearchAliasViewWithOptions(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = rndName(); + final SearchAliasCreateOptions options = new SearchAliasCreateOptions(); + final ViewEntity info = db.searchAlias(viewName).create(options).get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + assertThat(db.searchAlias(viewName).exists().get()).isTrue(); + } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void createSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) { -// assumeTrue(isAtLeastVersion(3, 10)); -// ArangoCollectionAsync col = db.collection(COLL_1); -// String idxName1 = rndName(); -// col.ensureInvertedIndex(new InvertedIndexOptions() -// .name(idxName1) -// .fields(new InvertedIndexField().name("a" + rnd()))); -// -// String idxName2 = rndName(); -// col.ensureInvertedIndex(new InvertedIndexOptions() -// .name(idxName2) -// .fields(new InvertedIndexField().name("a" + rnd()))); -// -// String viewName = rndName(); -// final SearchAliasCreateOptions options = new SearchAliasCreateOptions() -// .indexes( -// new SearchAliasIndex(COLL_1, idxName1, SearchAliasIndex.OperationType.add), -// new SearchAliasIndex(COLL_1, idxName2, SearchAliasIndex.OperationType.add), -// new SearchAliasIndex(COLL_1, idxName2, SearchAliasIndex.OperationType.del) -// ); -// final ViewEntity info = db.searchAlias(viewName).create(options); -// assertThat(info).isNotNull(); -// assertThat(info.getId()).isNotNull(); -// assertThat(info.getName()).isEqualTo(viewName); -// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); -// -// final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties(); -// assertThat(properties).isNotNull(); -// assertThat(properties.getId()).isNotNull(); -// assertThat(properties.getName()).isEqualTo(viewName); -// assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); -// assertThat(properties.getIndexes()) -// .isNotNull() -// .isNotEmpty() -// .anyMatch(i -> i.getCollection().equals(COLL_1) && i.getIndex().equals(idxName1)); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollectionAsync col = db.collection(COLL_1); + String idxName1 = rndName(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName1) + .fields(new InvertedIndexField().name("a" + rnd()))).get(); + + String idxName2 = rndName(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName2) + .fields(new InvertedIndexField().name("a" + rnd()))).get(); + + String viewName = rndName(); + final SearchAliasCreateOptions options = new SearchAliasCreateOptions() + .indexes( + new SearchAliasIndex(COLL_1, idxName1, SearchAliasIndex.OperationType.add), + new SearchAliasIndex(COLL_1, idxName2, SearchAliasIndex.OperationType.add), + new SearchAliasIndex(COLL_1, idxName2, SearchAliasIndex.OperationType.del) + ); + final ViewEntity info = db.searchAlias(viewName).create(options).get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + + final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties().get(); + assertThat(properties).isNotNull(); + assertThat(properties.getId()).isNotNull(); + assertThat(properties.getName()).isEqualTo(viewName); + assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + assertThat(properties.getIndexes()) + .isNotNull() + .isNotEmpty() + .anyMatch(i -> i.getCollection().equals(COLL_1) && i.getIndex().equals(idxName1)); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -311,45 +310,45 @@ void updateArangoSearchViewProperties(ArangoDatabaseAsync db) throws ExecutionEx assertThat(next.getStoreValues()).isEqualTo(StoreValuesType.ID); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void updateSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { -// assumeTrue(isAtLeastVersion(3, 10)); -// ArangoCollectionAsync col = db.collection(COLL_1); -// String idxName = rndName(); -// col.ensureInvertedIndex(new InvertedIndexOptions() -// .name(idxName) -// .fields(new InvertedIndexField().name("a" + rnd()))).get(); -// ArangoCollectionAsync col2 = db.collection(COLL_2); -// String idxName2 = rndName(); -// col2.ensureInvertedIndex(new InvertedIndexOptions() -// .name(idxName2) -// .fields(new InvertedIndexField().name("a" + rnd()))).get(); -// -// String viewName = rndName(); -// final SearchAliasCreateOptions options = new SearchAliasCreateOptions() -// .indexes(new SearchAliasIndex(COLL_1, idxName)); -// final ViewEntity info = db.searchAlias(viewName).create(options); -// db.searchAlias(viewName).updateProperties(new SearchAliasPropertiesOptions() -// .indexes(new SearchAliasIndex(COLL_2, idxName2))); -// -// assertThat(info).isNotNull(); -// assertThat(info.getId()).isNotNull(); -// assertThat(info.getName()).isEqualTo(viewName); -// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); -// -// final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties(); -// assertThat(properties).isNotNull(); -// assertThat(properties.getId()).isNotNull(); -// assertThat(properties.getName()).isEqualTo(viewName); -// assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); -// assertThat(properties.getIndexes()) -// .isNotNull() -// .isNotEmpty() -// .hasSize(2) -// .anyMatch(i -> i.getCollection().equals(COLL_1) && i.getIndex().equals(idxName)) -// .anyMatch(i -> i.getCollection().equals(COLL_2) && i.getIndex().equals(idxName2)); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void updateSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollectionAsync col = db.collection(COLL_1); + String idxName = rndName(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName) + .fields(new InvertedIndexField().name("a" + rnd()))).get(); + ArangoCollectionAsync col2 = db.collection(COLL_2); + String idxName2 = rndName(); + col2.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName2) + .fields(new InvertedIndexField().name("a" + rnd()))).get(); + + String viewName = rndName(); + final SearchAliasCreateOptions options = new SearchAliasCreateOptions() + .indexes(new SearchAliasIndex(COLL_1, idxName)); + final ViewEntity info = db.searchAlias(viewName).create(options).get(); + db.searchAlias(viewName).updateProperties(new SearchAliasPropertiesOptions() + .indexes(new SearchAliasIndex(COLL_2, idxName2))).get(); + + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + + final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties().get(); + assertThat(properties).isNotNull(); + assertThat(properties.getId()).isNotNull(); + assertThat(properties.getName()).isEqualTo(viewName); + assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + assertThat(properties.getIndexes()) + .isNotNull() + .isNotEmpty() + .hasSize(2) + .anyMatch(i -> i.getCollection().equals(COLL_1) && i.getIndex().equals(idxName)) + .anyMatch(i -> i.getCollection().equals(COLL_2) && i.getIndex().equals(idxName2)); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -370,44 +369,44 @@ void replaceArangoSearchViewProperties(ArangoDatabaseAsync db) throws ExecutionE assertThat(link.getFields().iterator().next().getName()).isEqualTo("value"); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void replaceSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { -// assumeTrue(isAtLeastVersion(3, 10)); -// ArangoCollectionAsync col = db.collection(COLL_1); -// String idxName = rndName(); -// col.ensureInvertedIndex(new InvertedIndexOptions() -// .name(idxName) -// .fields(new InvertedIndexField().name("a" + rnd()))).get(); -// ArangoCollectionAsync col2 = db.collection(COLL_2); -// String idxName2 = rndName(); -// col2.ensureInvertedIndex(new InvertedIndexOptions() -// .name(idxName2) -// .fields(new InvertedIndexField().name("a" + rnd()))).get(); -// -// String viewName = rndName(); -// final SearchAliasCreateOptions options = new SearchAliasCreateOptions() -// .indexes(new SearchAliasIndex(COLL_1, idxName)); -// final ViewEntity info = db.searchAlias(viewName).create(options); -// db.searchAlias(viewName).replaceProperties(new SearchAliasPropertiesOptions() -// .indexes(new SearchAliasIndex(COLL_2, idxName2))); -// -// assertThat(info).isNotNull(); -// assertThat(info.getId()).isNotNull(); -// assertThat(info.getName()).isEqualTo(viewName); -// assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); -// -// final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties(); -// assertThat(properties).isNotNull(); -// assertThat(properties.getId()).isNotNull(); -// assertThat(properties.getName()).isEqualTo(viewName); -// assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); -// assertThat(properties.getIndexes()) -// .isNotNull() -// .isNotEmpty() -// .hasSize(1) -// .anyMatch(i -> i.getCollection().equals(COLL_2) && i.getIndex().equals(idxName2)); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void replaceSearchAliasViewWithIndexesAndGetProperties(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollectionAsync col = db.collection(COLL_1); + String idxName = rndName(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName) + .fields(new InvertedIndexField().name("a" + rnd()))).get(); + ArangoCollectionAsync col2 = db.collection(COLL_2); + String idxName2 = rndName(); + col2.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName2) + .fields(new InvertedIndexField().name("a" + rnd()))).get(); + + String viewName = rndName(); + final SearchAliasCreateOptions options = new SearchAliasCreateOptions() + .indexes(new SearchAliasIndex(COLL_1, idxName)); + final ViewEntity info = db.searchAlias(viewName).create(options).get(); + db.searchAlias(viewName).replaceProperties(new SearchAliasPropertiesOptions() + .indexes(new SearchAliasIndex(COLL_2, idxName2))).get(); + + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + + final SearchAliasPropertiesEntity properties = db.searchAlias(viewName).getProperties().get(); + assertThat(properties).isNotNull(); + assertThat(properties.getId()).isNotNull(); + assertThat(properties.getName()).isEqualTo(viewName); + assertThat(properties.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + assertThat(properties.getIndexes()) + .isNotNull() + .isNotEmpty() + .hasSize(1) + .anyMatch(i -> i.getCollection().equals(COLL_2) && i.getIndex().equals(idxName2)); + } private void createGetAndDeleteTypedAnalyzer(ArangoDatabaseAsync db, SearchAnalyzer analyzer) throws ExecutionException, InterruptedException { diff --git a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java index 74b86326e..2c6d6981e 100644 --- a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java @@ -111,7 +111,7 @@ void insertVertexViolatingUniqueConstraint(ArangoVertexCollectionAsync vertices) ArangoCollectionAsync collection = vertices.graph().db().collection(vertices.name()); collection .ensurePersistentIndex(Collections.singletonList("field"), - new PersistentIndexOptions().unique(true).sparse(true)); + new PersistentIndexOptions().unique(true).sparse(true)).get(); VertexEntity inserted = vertices.insertVertex(RawJson.of("{\"field\": 99}")).get(); diff --git a/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java index ec804dca7..39bb4eeca 100644 --- a/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java @@ -119,7 +119,7 @@ void rename(ArangoDatabaseAsync db) throws ExecutionException, InterruptedExcept String oldName = rndName(); String newName = rndName(); - db.createView(oldName, ViewType.ARANGO_SEARCH); + db.createView(oldName, ViewType.ARANGO_SEARCH).get(); db.view(oldName).rename(newName).get(); assertThat(db.view(oldName).exists().get()).isFalse(); assertThat(db.view(newName).exists().get()).isTrue(); From 2a4740b0506f2f8bed533f21e2225e9c621f387b Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 17 Oct 2023 20:59:13 +0200 Subject: [PATCH 36/62] StreamTransactionAsyncTest --- .../arangodb/StreamTransactionAsyncTest.java | 819 ++++++++++++++++++ .../StreamTransactionConflictsAsyncTest.java | 120 +++ .../StreamTransactionGraphAsyncTest.java | 409 +++++++++ 3 files changed, 1348 insertions(+) create mode 100644 driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java create mode 100644 driver/src/test/java/com/arangodb/StreamTransactionConflictsAsyncTest.java create mode 100644 driver/src/test/java/com/arangodb/StreamTransactionGraphAsyncTest.java diff --git a/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java b/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java new file mode 100644 index 000000000..83686c94d --- /dev/null +++ b/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java @@ -0,0 +1,819 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.model.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Michele Rastelli + */ +class StreamTransactionAsyncTest extends BaseJunit5 { + + private static final String COLLECTION_NAME = "StreamTransactionTest_collection"; + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void beginStreamTransaction(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db.beginStreamTransaction(null).get(); + assertThat(tx.getId()).isNotNull(); + assertThat(tx.getStatus()).isEqualTo(StreamTransactionStatus.running); + db.abortStreamTransaction(tx.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void beginStreamTransactionWithNonExistingCollectionsShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + Throwable thrown = catchThrowable(() -> + db.beginStreamTransaction(new StreamTransactionOptions().writeCollections("notExistingCollection")).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void abortStreamTransaction(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity begunTx = db.beginStreamTransaction(null).get(); + StreamTransactionEntity abortedTx = db.abortStreamTransaction(begunTx.getId()).get(); + + assertThat(abortedTx.getId()).isNotNull(); + assertThat(abortedTx.getId()).isEqualTo(begunTx.getId()); + assertThat(abortedTx.getStatus()).isEqualTo(StreamTransactionStatus.aborted); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void abortStreamTransactionTwice(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity begunTx = db.beginStreamTransaction(null).get(); + db.abortStreamTransaction(begunTx.getId()); + db.abortStreamTransaction(begunTx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void abortStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + Throwable thrown = catchThrowable(() -> db.abortStreamTransaction("000000").get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void abortStreamTransactionWithInvalidTransactionIdShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + Throwable thrown = catchThrowable(() -> db.abortStreamTransaction("invalidTransactionId").get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void abortCommittedStreamTransactionShouldThrow(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null).get(); + db.commitStreamTransaction(createdTx.getId()).get(); + Throwable thrown = catchThrowable(() -> db.abortStreamTransaction(createdTx.getId()).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getStreamTransaction(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null).get(); + StreamTransactionEntity gotTx = db.getStreamTransaction(createdTx.getId()).get(); + + assertThat(gotTx.getId()).isNotNull(); + assertThat(gotTx.getId()).isEqualTo(createdTx.getId()); + assertThat(gotTx.getStatus()).isEqualTo(StreamTransactionStatus.running); + + db.abortStreamTransaction(createdTx.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + Throwable thrown = catchThrowable(() -> db.getStreamTransaction("000000").get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getStreamTransactionWithInvalidTransactionIdShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + Throwable thrown = catchThrowable(() -> db.getStreamTransaction("invalidTransactionId").get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void commitStreamTransaction(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null).get(); + StreamTransactionEntity committedTx = db.commitStreamTransaction(createdTx.getId()).get(); + + assertThat(committedTx.getId()).isNotNull(); + assertThat(committedTx.getId()).isEqualTo(createdTx.getId()); + assertThat(committedTx.getStatus()).isEqualTo(StreamTransactionStatus.committed); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void commitStreamTransactionTwice(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null).get(); + db.commitStreamTransaction(createdTx.getId()); + db.commitStreamTransaction(createdTx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void commitStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + Throwable thrown = catchThrowable(() -> db.commitStreamTransaction("000000").get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void commitStreamTransactionWithInvalidTransactionIdShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + Throwable thrown = catchThrowable(() -> db.commitStreamTransaction("invalidTransactionId").get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void commitAbortedStreamTransactionShouldThrow(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null).get(); + db.abortStreamTransaction(createdTx.getId()).get(); + Throwable thrown = catchThrowable(() -> db.commitStreamTransaction(createdTx.getId()).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getDocument(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)).get(); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = collection + .insertDocument(new BaseDocument(), null).get(); + + // assert that the document is not found from within the tx + assertThat(collection.getDocument(externalDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get()).isNull(); + + db.abortStreamTransaction(tx.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getDocumentWithNonExistingTransactionIdShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + Throwable thrown = catchThrowable(() -> collection + .getDocument("docId", BaseDocument.class, new DocumentReadOptions().streamTransactionId("123456")).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getDocumentWithInvalidTransactionIdShouldThrow(ArangoDatabaseAsync db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + Throwable thrown = catchThrowable(() -> collection + .getDocument("docId", BaseDocument.class, new DocumentReadOptions().streamTransactionId("abcde")).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getDocuments(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)).get(); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // insert documents from outside the tx + DocumentCreateEntity externalDoc1 = collection + .insertDocument(new BaseDocument(), null).get(); + + DocumentCreateEntity externalDoc2 = collection + .insertDocument(new BaseDocument(), null).get(); + + // assert that the documents are not found from within the tx + MultiDocumentEntity documents = collection + .getDocuments(Arrays.asList(externalDoc1.getId(), externalDoc2.getId()), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get(); + + assertThat(documents.getDocuments()).isEmpty(); + + db.abortStreamTransaction(tx.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void insertDocument(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // insert a document from within the tx + DocumentCreateEntity txDoc = collection + .insertDocument(new BaseDocument(), new DocumentCreateOptions().streamTransactionId(tx.getId())).get(); + + // assert that the document is not found from outside the tx + assertThat(collection.getDocument(txDoc.getKey(), BaseDocument.class, null).get()).isNull(); + + // assert that the document is found from within the tx + assertThat(collection.getDocument(txDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get()).isNotNull(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the document is found after commit + assertThat(collection.getDocument(txDoc.getKey(), BaseDocument.class, null).get()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void insertDocuments(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // insert documents from within the tx + MultiDocumentEntity> txDocs = collection + .insertDocuments(Arrays.asList(new BaseDocument(), new BaseDocument(), new BaseDocument()), + new DocumentCreateOptions().streamTransactionId(tx.getId()), BaseDocument.class).get(); + + List keys = txDocs.getDocuments().stream().map(DocumentEntity::getKey).collect(Collectors.toList()); + + // assert that the documents are not found from outside the tx + assertThat(collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments()).isEmpty(); + + // assert that the documents are found from within the tx + assertThat(collection + .getDocuments(keys, BaseDocument.class, new DocumentReadOptions().streamTransactionId(tx.getId())).get() + .getDocuments()).hasSize(keys.size()); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the document is found after commit + assertThat(collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments()).hasSize(keys.size()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void replaceDocument(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("test", "foo"); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + DocumentCreateEntity createdDoc = collection.insertDocument(doc, null).get(); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + // replace document from within the tx + doc.updateAttribute("test", "bar"); + collection.replaceDocument(createdDoc.getKey(), doc, + new DocumentReplaceOptions().streamTransactionId(tx.getId())).get(); + + // assert that the document has not been replaced from outside the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null).get() + .getProperties()).containsEntry("test", "foo"); + + // assert that the document has been replaced from within the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get().getProperties()).containsEntry("test", + "bar"); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the document has been replaced after commit + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null).get() + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void replaceDocuments(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + List docs = IntStream.range(0, 3).mapToObj(it -> new BaseDocument()) + .peek(doc -> doc.addAttribute("test", "foo")).collect(Collectors.toList()); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + List createdDocs = collection + .insertDocuments(docs, new DocumentCreateOptions().returnNew(true), BaseDocument.class).get().getDocuments().stream() + .map(DocumentCreateEntity::getNew).collect(Collectors.toList()); + + List keys = createdDocs.stream().map(BaseDocument::getKey).collect(Collectors.toList()); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + List modifiedDocs = createdDocs.stream().peek(doc -> doc.updateAttribute("test", "bar")).collect(Collectors.toList()); + + // replace document from within the tx + collection + .replaceDocuments(modifiedDocs, new DocumentReplaceOptions().streamTransactionId(tx.getId())).get(); + + // assert that the documents has not been replaced from outside the tx + collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments().stream() + .map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("foo")); + + // assert that the document has been replaced from within the tx + collection + .getDocuments(keys, BaseDocument.class, new DocumentReadOptions().streamTransactionId(tx.getId())).get() + .getDocuments().stream().map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("bar")); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the document has been replaced after commit + collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments().stream() + .map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("bar")); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void updateDocument(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("test", "foo"); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + DocumentCreateEntity createdDoc = collection.insertDocument(doc, null).get(); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + // update document from within the tx + doc.updateAttribute("test", "bar"); + collection + .updateDocument(createdDoc.getKey(), doc, new DocumentUpdateOptions().streamTransactionId(tx.getId())); + + // assert that the document has not been updated from outside the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null).get() + .getProperties()).containsEntry("test", "foo"); + + // assert that the document has been updated from within the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get().getProperties()).containsEntry("test", "bar") + ; + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the document has been updated after commit + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null).get() + .getProperties()).containsEntry("test", "bar"); + + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void updateDocuments(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + List docs = IntStream.range(0, 3).mapToObj(it -> new BaseDocument()) + .peek(doc -> doc.addAttribute("test", "foo")).collect(Collectors.toList()); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + List createdDocs = collection + .insertDocuments(docs, new DocumentCreateOptions().returnNew(true), BaseDocument.class).get().getDocuments().stream() + .map(DocumentCreateEntity::getNew).collect(Collectors.toList()); + + List keys = createdDocs.stream().map(BaseDocument::getKey).collect(Collectors.toList()); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + List modifiedDocs = createdDocs.stream().peek(doc -> doc.updateAttribute("test", "bar")).collect(Collectors.toList()); + + // update documents from within the tx + collection + .updateDocuments(modifiedDocs, new DocumentUpdateOptions().streamTransactionId(tx.getId())).get(); + + // assert that the documents have not been updated from outside the tx + collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments().stream() + .map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("foo")); + + // assert that the documents have been updated from within the tx + collection + .getDocuments(keys, BaseDocument.class, new DocumentReadOptions().streamTransactionId(tx.getId())).get() + .getDocuments().stream().map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("bar")); + + db.commitStreamTransaction(tx.getId()); + + // assert that the document has been updated after commit + collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments().stream() + .map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("bar")); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void deleteDocument(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + DocumentCreateEntity createdDoc = collection + .insertDocument(new BaseDocument(), null).get(); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + // delete document from within the tx + collection + .deleteDocument(createdDoc.getKey(), new DocumentDeleteOptions().streamTransactionId(tx.getId())).get(); + + // assert that the document has not been deleted from outside the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null).get()).isNotNull(); + + // assert that the document has been deleted from within the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get()).isNull(); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the document has been deleted after commit + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null).get()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void deleteDocuments(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + List keys = collection + .insertDocuments(Arrays.asList(new BaseDocument(), new BaseDocument(), new BaseDocument())).get() + .getDocuments().stream().map(DocumentEntity::getKey).collect(Collectors.toList()); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + // delete document from within the tx + collection + .deleteDocuments(keys, new DocumentDeleteOptions().streamTransactionId(tx.getId())).get(); + + // assert that the documents has not been deleted from outside the tx + assertThat(collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments()).hasSize(keys.size()); + + // assert that the document has been deleted from within the tx + assertThat(collection + .getDocuments(keys, BaseDocument.class, new DocumentReadOptions().streamTransactionId(tx.getId())).get() + .getDocuments()).isEmpty(); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the document has been deleted after commit + assertThat(collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void documentExists(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)).get(); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = collection + .insertDocument(new BaseDocument(), null).get(); + + // assert that the document is not found from within the tx + assertThat(collection + .documentExists(externalDoc.getKey(), new DocumentExistsOptions().streamTransactionId(tx.getId())).get()).isFalse(); + + db.abortStreamTransaction(tx.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void count(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + Long initialCount = collection.count().get().getCount(); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)).get(); + + // insert a document from outside the tx + collection.insertDocument(new BaseDocument(), null).get(); + + // assert that the document is not counted from within the tx + assertThat(collection.count(new CollectionCountOptions().streamTransactionId(tx.getId())).get() + .getCount()).isEqualTo(initialCount); + + db.abortStreamTransaction(tx.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void truncate(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + collection.insertDocument(new BaseDocument(), null).get(); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + // truncate document from within the tx + collection.truncate(new CollectionTruncateOptions().streamTransactionId(tx.getId())).get(); + + // assert that the collection has not been truncated from outside the tx + assertThat(collection.count().get().getCount()).isPositive(); + + // assert that the collection has been truncated from within the tx + assertThat(collection.count(new CollectionCountOptions().streamTransactionId(tx.getId())).get() + .getCount()).isZero(); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the collection has been truncated after commit + assertThat(collection.count().get().getCount()).isZero(); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void createCursor(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { +// assumeTrue(isSingleServer()); +// assumeTrue(isAtLeastVersion(3, 5)); +// assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); +// +// StreamTransactionEntity tx = db +// .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)).get(); +// ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); +// +// // insert a document from outside the tx +// DocumentCreateEntity externalDoc = collection +// .insertDocument(new BaseDocument(), null).get(); +// +// final Map bindVars = new HashMap<>(); +// bindVars.put("@collection", COLLECTION_NAME); +// bindVars.put("key", externalDoc.getKey()); +// +// ArangoCursor cursor = db +// .query("FOR doc IN @@collection FILTER doc._key == @key RETURN doc", BaseDocument.class, bindVars, +// new AqlQueryOptions().streamTransactionId(tx.getId())); +// +// // assert that the document is not found from within the tx +// assertThat(cursor.hasNext()).isFalse(); +// +// db.abortStreamTransaction(tx.getId()); +// } +// +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void nextCursor(ArangoDatabaseAsync db) { +// assumeTrue(isSingleServer()); +// assumeTrue(isAtLeastVersion(3, 5)); +// assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); +// +// StreamTransactionEntity tx = db.beginStreamTransaction( +// new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); +// ArangoCollection collection = db.collection(COLLECTION_NAME); +// +// // insert documents from within the tx +// List keys = collection +// .insertDocuments(IntStream.range(0, 10).mapToObj(it -> new BaseDocument()).collect(Collectors.toList()), +// new DocumentCreateOptions().streamTransactionId(tx.getId())).getDocuments().stream() +// .map(DocumentEntity::getKey).collect(Collectors.toList()); +// +// final Map bindVars = new HashMap<>(); +// bindVars.put("@collection", COLLECTION_NAME); +// bindVars.put("keys", keys); +// +// ArangoCursor cursor = db +// .query("FOR doc IN @@collection FILTER CONTAINS_ARRAY(@keys, doc._key) RETURN doc", BaseDocument.class, bindVars, +// new AqlQueryOptions().streamTransactionId(tx.getId()).batchSize(2)); +// +// List docs = cursor.asListRemaining(); +// +// // assert that all the keys are returned from the query +// assertThat(docs.stream().map(BaseDocument::getKey).collect(Collectors.toList())).containsAll(keys); +// +// db.abortStreamTransaction(tx.getId()); +// } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getStreamTransactions(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx1 = db.beginStreamTransaction(null).get(); + StreamTransactionEntity tx2 = db.beginStreamTransaction(null).get(); + + List createdIds = Arrays.asList(tx1.getId(), tx2.getId()); + Set gotTxs = db.getStreamTransactions().get().stream(). + filter(it -> createdIds.contains(it.getId())).collect(Collectors.toSet()); + + assertThat(gotTxs).hasSameSizeAs(createdIds); + assertThat(gotTxs.stream() + .allMatch(it -> it.getState() == StreamTransactionStatus.running)).isTrue(); + + db.abortStreamTransaction(tx1.getId()).get(); + db.abortStreamTransaction(tx2.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionAllowImplicitFalse(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().allowImplicit(false)).get(); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = collection + .insertDocument(new BaseDocument(), null).get(); + + // assert that we cannot read from collection + Throwable thrown = catchThrowable(() -> collection.getDocument(externalDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(400); + assertThat(e.getErrorNum()).isEqualTo(1652); + assertThat(e.getMessage()).contains("unregistered collection used in transaction"); + + db.abortStreamTransaction(tx.getId()).get(); + } + +// @ParameterizedTest(name = "{index}") +// @MethodSource("asyncDbs") +// void transactionDirtyRead(ArangoDatabaseAsync db) throws IOException, ExecutionException, InterruptedException { +// assumeTrue(isCluster()); +// assumeTrue(isAtLeastVersion(3, 10)); +// +// ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); +// DocumentCreateEntity doc = collection.insertDocument(new BaseDocument()).get(); +// +// StreamTransactionEntity tx = db +// .beginStreamTransaction(new StreamTransactionOptions() +// .readCollections(COLLECTION_NAME) +// .allowDirtyRead(true)).get(); +// +// MultiDocumentEntity readDocs = collection.getDocuments(Collections.singletonList(doc.getKey()), +// BaseDocument.class, +// new DocumentReadOptions().streamTransactionId(tx.getId())).get(); +// +// assertThat(readDocs.isPotentialDirtyRead()).isTrue(); +// assertThat(readDocs.getDocuments()).hasSize(1); +// +// final ArangoCursor cursor = db.query("FOR i IN @@col RETURN i", BaseDocument.class, +// Collections.singletonMap("@col", COLLECTION_NAME), +// new AqlQueryOptions().streamTransactionId(tx.getId())); +// assertThat(cursor.isPotentialDirtyRead()).isTrue(); +// cursor.close(); +// +// db.abortStreamTransaction(tx.getId()); +// } + +} diff --git a/driver/src/test/java/com/arangodb/StreamTransactionConflictsAsyncTest.java b/driver/src/test/java/com/arangodb/StreamTransactionConflictsAsyncTest.java new file mode 100644 index 000000000..68ba9a5e7 --- /dev/null +++ b/driver/src/test/java/com/arangodb/StreamTransactionConflictsAsyncTest.java @@ -0,0 +1,120 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.ArangoDBEngine; +import com.arangodb.entity.BaseDocument; +import com.arangodb.entity.StreamTransactionEntity; +import com.arangodb.model.DocumentCreateOptions; +import com.arangodb.model.StreamTransactionOptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Michele Rastelli + */ +class StreamTransactionConflictsAsyncTest extends BaseJunit5 { + + private static final String COLLECTION_NAME = "db_concurrent_stream_transactions_test-" + UUID.randomUUID(); + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void conflictOnInsertDocumentWithNotYetCommittedTx(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx1 = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + StreamTransactionEntity tx2 = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + String key = UUID.randomUUID().toString(); + + // insert a document from within tx1 + db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(key), new DocumentCreateOptions().streamTransactionId(tx1.getId())).get(); + + // insert conflicting document from within tx2 + Throwable thrown = catchThrowable(() -> db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(key), + new DocumentCreateOptions().streamTransactionId(tx2.getId())).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + + if (isAtLeastVersion(3, 8)) { + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + db.abortStreamTransaction(tx1.getId()).get(); + db.abortStreamTransaction(tx2.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void conflictOnInsertDocumentWithAlreadyCommittedTx(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx1 = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + StreamTransactionEntity tx2 = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + + String key = UUID.randomUUID().toString(); + + // insert a document from within tx1 + db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(key), new DocumentCreateOptions().streamTransactionId(tx1.getId())).get(); + + // commit tx1 + db.commitStreamTransaction(tx1.getId()).get(); + + // insert conflicting document from within tx2 + Throwable thrown = catchThrowable(() -> db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(key), + new DocumentCreateOptions().streamTransactionId(tx2.getId())).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + if (isAtLeastVersion(3, 8)) { + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + db.abortStreamTransaction(tx2.getId()); + } +} diff --git a/driver/src/test/java/com/arangodb/StreamTransactionGraphAsyncTest.java b/driver/src/test/java/com/arangodb/StreamTransactionGraphAsyncTest.java new file mode 100644 index 000000000..2f15cb1a5 --- /dev/null +++ b/driver/src/test/java/com/arangodb/StreamTransactionGraphAsyncTest.java @@ -0,0 +1,409 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.model.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Michele Rastelli + */ +class StreamTransactionGraphAsyncTest extends BaseJunit5 { + + private static final String GRAPH_NAME = "graph_stream_transaction_graph_test"; + private static final String EDGE_COLLECTION = "edge_collection_stream_transaction_graph_test"; + private static final String VERTEX_COLLECTION_1 = "vertex_collection_1_stream_transaction_graph_test"; + private static final String VERTEX_COLLECTION_2 = "vertex_collection_2_stream_transaction_graph_test"; + + private static Stream asyncVertices() { + return asyncDbsStream() + .map(db -> db.graph(GRAPH_NAME).vertexCollection(VERTEX_COLLECTION_1)) + .map(Arguments::of); + } + + private static Stream asyncEdges() { + return asyncDbsStream() + .map(db -> db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION)) + .map(Arguments::of); + } + + @BeforeAll + static void init() { + initDB(); + initGraph(GRAPH_NAME, Collections.singletonList(new EdgeDefinition() + .collection(EDGE_COLLECTION).from(VERTEX_COLLECTION_1).to(VERTEX_COLLECTION_2) + ), null); + } + + private BaseEdgeDocument createEdgeValue(String streamTransactionId, ArangoGraphAsync graph) throws ExecutionException, InterruptedException { + ArangoVertexCollectionAsync vertexCollection1 = graph.vertexCollection(VERTEX_COLLECTION_1); + ArangoVertexCollectionAsync vertexCollection2 = graph.vertexCollection(VERTEX_COLLECTION_2); + VertexEntity v1 = vertexCollection1.insertVertex(new BaseDocument(), + new VertexCreateOptions().streamTransactionId(streamTransactionId)).get(); + VertexEntity v2 = vertexCollection2.insertVertex(new BaseDocument(), + new VertexCreateOptions().streamTransactionId(streamTransactionId)).get(); + BaseEdgeDocument value = new BaseEdgeDocument(); + value.setFrom(v1.getId()); + value.setTo(v2.getId()); + return value; + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void getVertex(ArangoVertexCollectionAsync vertexCollection1) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoDatabaseAsync db = vertexCollection1.graph().db(); + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // insert a vertex from outside the tx + VertexEntity createdVertex = vertexCollection1.insertVertex(new BaseDocument()).get(); + + // assert that the vertex is not found from within the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get()).isNull(); + + db.abortStreamTransaction(tx.getId()).get(); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void createVertex(ArangoVertexCollectionAsync vertexCollection1) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoDatabaseAsync db = vertexCollection1.graph().db(); + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // insert a vertex from within the tx + VertexEntity createdVertex = vertexCollection1.insertVertex(new BaseDocument(), + new VertexCreateOptions().streamTransactionId(tx.getId())).get(); + + // assert that the vertex is not found from outside the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, null).get()).isNull(); + + // assert that the vertex is found from within the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get()).isNotNull(); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the vertex is found after commit + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, null).get()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void replaceVertex(ArangoVertexCollectionAsync vertexCollection1) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("test", "foo"); + + VertexEntity createdVertex = vertexCollection1.insertVertex(doc, null).get(); + + ArangoDatabaseAsync db = vertexCollection1.graph().db(); + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // replace vertex from within the tx + doc.updateAttribute("test", "bar"); + vertexCollection1.replaceVertex(createdVertex.getKey(), doc, + new VertexReplaceOptions().streamTransactionId(tx.getId())).get(); + + // assert that the vertex has not been replaced from outside the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, null).get() + .getProperties()).containsEntry("test", "foo"); + + // assert that the vertex has been replaced from within the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get().getProperties()).containsEntry("test" + , "bar"); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the vertex has been replaced after commit + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, null).get() + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void updateVertex(ArangoVertexCollectionAsync vertexCollection1) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("test", "foo"); + + VertexEntity createdDoc = vertexCollection1.insertVertex(doc, null).get(); + + ArangoDatabaseAsync db = vertexCollection1.graph().db(); + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // update vertex from within the tx + doc.updateAttribute("test", "bar"); + vertexCollection1.updateVertex(createdDoc.getKey(), doc, + new VertexUpdateOptions().streamTransactionId(tx.getId())).get(); + + // assert that the vertex has not been updated from outside the tx + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, null).get() + .getProperties()).containsEntry("test", "foo"); + + // assert that the vertex has been updated from within the tx + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get().getProperties()).containsEntry("test" + , "bar"); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the vertex has been updated after commit + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, null).get() + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncVertices") + void deleteVertex(ArangoVertexCollectionAsync vertexCollection1) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + VertexEntity createdDoc = vertexCollection1.insertVertex(new BaseDocument(), null).get(); + + ArangoDatabaseAsync db = vertexCollection1.graph().db(); + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // delete vertex from within the tx + vertexCollection1.deleteVertex(createdDoc.getKey(), new VertexDeleteOptions().streamTransactionId(tx.getId())).get(); + + // assert that the vertex has not been deleted from outside the tx + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, null).get()).isNotNull(); + + // assert that the vertex has been deleted from within the tx + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get()).isNull(); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the vertex has been deleted after commit + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, null).get()).isNull(); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncEdges") + void getEdge(ArangoEdgeCollectionAsync edgeCollection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoDatabaseAsync db = edgeCollection.graph().db(); + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // insert an edge from outside the tx + EdgeEntity createdEdge = edgeCollection.insertEdge(createEdgeValue(null, edgeCollection.graph())).get(); + + // assert that the edge is not found from within the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get()).isNull(); + + db.abortStreamTransaction(tx.getId()).get(); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncEdges") + void createEdge(ArangoEdgeCollectionAsync edgeCollection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoDatabaseAsync db = edgeCollection.graph().db(); + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // insert an edge from within the tx + EdgeEntity createdEdge = edgeCollection.insertEdge(createEdgeValue(tx.getId(), edgeCollection.graph()), + new EdgeCreateOptions().streamTransactionId(tx.getId())).get(); + + // assert that the edge is not found from outside the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, null).get()).isNull(); + + // assert that the edge is found from within the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get()).isNotNull(); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the edge is found after commit + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, null).get()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncEdges") + void replaceEdge(ArangoEdgeCollectionAsync edgeCollection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseEdgeDocument doc = createEdgeValue(null, edgeCollection.graph()); + doc.addAttribute("test", "foo"); + + EdgeEntity createdEdge = edgeCollection.insertEdge(doc, null).get(); + + ArangoDatabaseAsync db = edgeCollection.graph().db(); + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // replace edge from within the tx + doc.updateAttribute("test", "bar"); + edgeCollection.replaceEdge(createdEdge.getKey(), doc, + new EdgeReplaceOptions().streamTransactionId(tx.getId())).get(); + + // assert that the edge has not been replaced from outside the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, null).get() + .getProperties()).containsEntry("test", "foo"); + + // assert that the edge has been replaced from within the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get().getProperties()).containsEntry("test" + , "bar"); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the edge has been replaced after commit + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, null).get() + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncEdges") + void updateEdge(ArangoEdgeCollectionAsync edgeCollection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseEdgeDocument doc = createEdgeValue(null, edgeCollection.graph()); + doc.addAttribute("test", "foo"); + + EdgeEntity createdDoc = edgeCollection.insertEdge(doc, null).get(); + + ArangoDatabaseAsync db = edgeCollection.graph().db(); + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // update edge from within the tx + doc.updateAttribute("test", "bar"); + edgeCollection.updateEdge(createdDoc.getKey(), doc, new EdgeUpdateOptions().streamTransactionId(tx.getId())).get(); + + // assert that the edge has not been updated from outside the tx + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, null).get() + .getProperties()).containsEntry("test", "foo"); + + // assert that the edge has been updated from within the tx + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get().getProperties()).containsEntry("test" + , "bar"); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the edge has been updated after commit + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, null).get() + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncEdges") + void deleteEdge(ArangoEdgeCollectionAsync edgeCollection) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + EdgeEntity createdDoc = edgeCollection.insertEdge(createEdgeValue(null, edgeCollection.graph()), null).get(); + + ArangoDatabaseAsync db = edgeCollection.graph().db(); + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions() + .readCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION) + .writeCollections(VERTEX_COLLECTION_1, VERTEX_COLLECTION_2, EDGE_COLLECTION)).get(); + + // delete edge from within the tx + edgeCollection.deleteEdge(createdDoc.getKey(), new EdgeDeleteOptions().streamTransactionId(tx.getId())).get(); + + // assert that the edge has not been deleted from outside the tx + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, null).get()).isNotNull(); + + // assert that the edge has been deleted from within the tx + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId())).get()).isNull(); + + db.commitStreamTransaction(tx.getId()).get(); + + // assert that the edge has been deleted after commit + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, null).get()).isNull(); + } + +} From 5effe9a40b2cec915be9ab24c751f26b999092f4 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 17 Oct 2023 22:42:10 +0200 Subject: [PATCH 37/62] test fixes --- .../java/com/arangodb/ArangoVertexCollectionAsyncTest.java | 2 +- .../test/java/com/arangodb/StreamTransactionAsyncTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java index 2c6d6981e..8f6034afb 100644 --- a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java @@ -116,7 +116,7 @@ void insertVertexViolatingUniqueConstraint(ArangoVertexCollectionAsync vertices) VertexEntity inserted = vertices.insertVertex(RawJson.of("{\"field\": 99}")).get(); try { - vertices.insertVertex(RawJson.of("{\"field\": 99}")); + vertices.insertVertex(RawJson.of("{\"field\": 99}")).get(); } catch (ArangoDBException e) { assertThat(e.getResponseCode()).isEqualTo(409); assertThat(e.getErrorNum()).isEqualTo(1210); diff --git a/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java b/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java index 83686c94d..b9da09db1 100644 --- a/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java @@ -334,7 +334,7 @@ void insertDocument(ArangoDatabaseAsync db) throws ExecutionException, Interrupt assertThat(collection.getDocument(txDoc.getKey(), BaseDocument.class, new DocumentReadOptions().streamTransactionId(tx.getId())).get()).isNotNull(); - db.commitStreamTransaction(tx.getId()); + db.commitStreamTransaction(tx.getId()).get(); // assert that the document is found after commit assertThat(collection.getDocument(txDoc.getKey(), BaseDocument.class, null).get()).isNotNull(); @@ -529,7 +529,7 @@ void updateDocuments(ArangoDatabaseAsync db) throws ExecutionException, Interrup .getDocuments().stream().map(it -> ((String) it.getAttribute("test"))) .forEach(it -> assertThat(it).isEqualTo("bar")); - db.commitStreamTransaction(tx.getId()); + db.commitStreamTransaction(tx.getId()).get(); // assert that the document has been updated after commit collection.getDocuments(keys, BaseDocument.class, null).get().getDocuments().stream() From 99e3b91259aaf85e703b3d55927a206dfb4094d3 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 10:47:49 +0200 Subject: [PATCH 38/62] unwrap CompletionException --- .../java/com/arangodb/ArangoDBException.java | 4 ++++ .../internal/ArangoCollectionAsyncImpl.java | 20 ++++++++++--------- .../internal/ArangoDatabaseAsyncImpl.java | 11 +++++----- .../com/arangodb/internal/ArangoExecutor.java | 2 +- .../internal/ArangoGraphAsyncImpl.java | 9 +++++---- .../internal/ArangoSearchAsyncImpl.java | 9 +++++---- .../internal/ArangoViewAsyncImpl.java | 9 +++++---- .../internal/SearchAliasAsyncImpl.java | 9 +++++---- .../internal/net/CommunicationProtocol.java | 8 +------- .../com/arangodb/internal/net/HostImpl.java | 2 +- .../internal/serde/InternalSerdeImpl.java | 14 ++++++------- .../arangodb/internal/serde/SerdeUtils.java | 4 ++-- .../arangodb/internal/util/EncodeUtils.java | 2 +- .../arangodb/internal/util/ResponseUtils.java | 4 ++-- .../ArangoVertexCollectionAsyncTest.java | 11 +++++----- .../com/arangodb/http/HttpCommunication.java | 3 ++- .../com/arangodb/http/HttpConnection.java | 2 +- .../com/arangodb/vst/VstCommunication.java | 2 +- .../arangodb/vst/VstCommunicationAsync.java | 20 +++++++++---------- .../arangodb/vst/internal/MessageStore.java | 2 +- .../arangodb/vst/internal/VstConnection.java | 4 ++-- 21 files changed, 78 insertions(+), 73 deletions(-) diff --git a/core/src/main/java/com/arangodb/ArangoDBException.java b/core/src/main/java/com/arangodb/ArangoDBException.java index 95f1921d0..2f0ebfdae 100644 --- a/core/src/main/java/com/arangodb/ArangoDBException.java +++ b/core/src/main/java/com/arangodb/ArangoDBException.java @@ -22,6 +22,8 @@ import com.arangodb.entity.ErrorEntity; +import java.util.concurrent.CompletionException; + /** * @author Mark Vollmary */ @@ -89,6 +91,8 @@ public static ArangoDBException wrap(Throwable t) { } else { return wrap(t.getCause()); } + } else if (t instanceof CompletionException) { + return wrap(t.getCause()); } else { return new ArangoDBException(t); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java index 06b4944ab..222b9e696 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java @@ -319,13 +319,14 @@ public CompletableFuture documentExists(final String key, final Documen .thenApply(Objects::nonNull); } - T catchGetDocumentExceptions(Throwable e) { - if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { - ArangoDBException arangoDBException = (ArangoDBException) e.getCause(); + T catchGetDocumentExceptions(Throwable err) { + Throwable e = err instanceof CompletionException ? err.getCause() : err; + if (e instanceof ArangoDBException) { + ArangoDBException arangoDBException = (ArangoDBException) e; // handle Response: 404, Error: 1655 - transaction not found if (arangoDBException.getErrorNum() != null && arangoDBException.getErrorNum() == 1655) { - throw (CompletionException) e; + throw (ArangoDBException) e; } if ((arangoDBException.getResponseCode() != null && (arangoDBException.getResponseCode() == 404 || arangoDBException.getResponseCode() == 304 @@ -333,7 +334,7 @@ T catchGetDocumentExceptions(Throwable e) { return null; } } - throw new CompletionException(e); + throw ArangoDBException.wrap(e); } @Override @@ -396,14 +397,15 @@ public CompletableFuture> getInvertedIndexes() { public CompletableFuture exists() { return getInfo() .thenApply(Objects::nonNull) - .exceptionally(e -> { - if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { - ArangoDBException aEx = (ArangoDBException) e.getCause(); + .exceptionally(err -> { + Throwable e = err instanceof CompletionException ? err.getCause() : err; + if (e instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e; if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(aEx.getErrorNum())) { return false; } } - throw new CompletionException(e); + throw ArangoDBException.wrap(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index 94c111a93..bb49e0f08 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -66,19 +66,20 @@ public CompletableFuture getEngine() { @Override public CompletableFuture exists() { - return getInfo().handle((result, ex) -> { + return getInfo().handle((result, err) -> { if (result != null) { return true; } - if (ex instanceof CompletionException && ex.getCause() instanceof ArangoDBException) { - ArangoDBException e = (ArangoDBException) ex.getCause(); - if (ArangoErrors.ERROR_ARANGO_DATABASE_NOT_FOUND.equals(e.getErrorNum())) { + Throwable e = err instanceof CompletionException ? err.getCause() : err; + if (e instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e; + if (ArangoErrors.ERROR_ARANGO_DATABASE_NOT_FOUND.equals(aEx.getErrorNum())) { return false; } } - throw new CompletionException(ex); + throw ArangoDBException.wrap(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecutor.java b/core/src/main/java/com/arangodb/internal/ArangoExecutor.java index da072eb98..bccc7880c 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecutor.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecutor.java @@ -50,7 +50,7 @@ public void disconnect() { try { protocol.close(); } catch (final IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java index 966a5cd7b..f6653ee6e 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java @@ -50,14 +50,15 @@ public ArangoDatabaseAsync db() { public CompletableFuture exists() { return getInfo() .thenApply(Objects::nonNull) - .exceptionally(e -> { - if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { - ArangoDBException aEx = (ArangoDBException) e.getCause(); + .exceptionally(err -> { + Throwable e = err instanceof CompletionException ? err.getCause() : err; + if (e instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e; if (ArangoErrors.ERROR_GRAPH_NOT_FOUND.equals(aEx.getErrorNum())) { return false; } } - throw new CompletionException(e); + throw ArangoDBException.wrap(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java index 70a540433..ed89e13c7 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java @@ -50,14 +50,15 @@ public ArangoDatabaseAsync db() { public CompletableFuture exists() { return getInfo() .thenApply(Objects::nonNull) - .exceptionally(e -> { - if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { - ArangoDBException aEx = (ArangoDBException) e.getCause(); + .exceptionally(err -> { + Throwable e = err instanceof CompletionException ? err.getCause() : err; + if (e instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e; if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(aEx.getErrorNum())) { return false; } } - throw new CompletionException(e); + throw ArangoDBException.wrap(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java index 800c1ad43..17888e598 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java @@ -46,14 +46,15 @@ public ArangoDatabaseAsync db() { public CompletableFuture exists() { return getInfo() .thenApply(Objects::nonNull) - .exceptionally(e -> { - if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { - ArangoDBException aEx = (ArangoDBException) e.getCause(); + .exceptionally(err -> { + Throwable e = err instanceof CompletionException ? err.getCause() : err; + if (e instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e; if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(aEx.getErrorNum())) { return false; } } - throw new CompletionException(e); + throw ArangoDBException.wrap(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java b/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java index 721edb5d9..9928e2749 100644 --- a/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java @@ -50,14 +50,15 @@ public ArangoDatabaseAsync db() { public CompletableFuture exists() { return getInfo() .thenApply(Objects::nonNull) - .exceptionally(e -> { - if (e instanceof CompletionException && e.getCause() instanceof ArangoDBException) { - ArangoDBException aEx = (ArangoDBException) e.getCause(); + .exceptionally(err -> { + Throwable e = err instanceof CompletionException ? err.getCause() : err; + if (e instanceof ArangoDBException) { + ArangoDBException aEx = (ArangoDBException) e; if (ArangoErrors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.equals(aEx.getErrorNum())) { return false; } } - throw new CompletionException(e); + throw ArangoDBException.wrap(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java b/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java index 2f94f3473..e35f917cc 100644 --- a/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java +++ b/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java @@ -26,7 +26,6 @@ import java.io.Closeable; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; /** @@ -41,12 +40,7 @@ default InternalResponse execute(final InternalRequest request, final HostHandle Thread.currentThread().interrupt(); throw ArangoDBException.wrap(e); } catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof CompletionException) { - throw ArangoDBException.wrap(cause.getCause()); - } else { - throw ArangoDBException.wrap(cause); - } + throw ArangoDBException.wrap(e.getCause()); } } diff --git a/core/src/main/java/com/arangodb/internal/net/HostImpl.java b/core/src/main/java/com/arangodb/internal/net/HostImpl.java index 2477bee9c..f0dde825f 100644 --- a/core/src/main/java/com/arangodb/internal/net/HostImpl.java +++ b/core/src/main/java/com/arangodb/internal/net/HostImpl.java @@ -60,7 +60,7 @@ public void closeOnError() { try { connectionPool.close(); } catch (final IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } diff --git a/core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java b/core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java index 462d461bc..159f53ac5 100644 --- a/core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java +++ b/core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java @@ -51,7 +51,7 @@ public byte[] serialize(final Object value) { try { return mapper.writeValueAsBytes(value); } catch (JsonProcessingException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } @@ -65,7 +65,7 @@ public String toJsonString(final byte[] content) { try { return SerdeUtils.INSTANCE.writeJson(mapper.readTree(content)); } catch (IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } @@ -75,7 +75,7 @@ public byte[] extract(final byte[] content, final String jsonPointer) { JsonNode target = parse(content).at(jsonPointer); return mapper.writeValueAsBytes(target); } catch (IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } @@ -84,7 +84,7 @@ public JsonNode parse(byte[] content) { try { return mapper.readTree(content); } catch (IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } @@ -93,7 +93,7 @@ public JsonNode parse(byte[] content, String jsonPointer) { try { return mapper.readTree(content).at(jsonPointer); } catch (IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } @@ -153,7 +153,7 @@ public T deserialize(final JsonNode node, final Type type) { try { return mapper.readerFor(mapper.constructType(type)).readValue(node); } catch (IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } @@ -165,7 +165,7 @@ public T deserialize(final byte[] content, final Type type) { try { return mapper.readerFor(mapper.constructType(type)).readValue(content); } catch (IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } diff --git a/core/src/main/java/com/arangodb/internal/serde/SerdeUtils.java b/core/src/main/java/com/arangodb/internal/serde/SerdeUtils.java index 74d888d58..d9dac1da8 100644 --- a/core/src/main/java/com/arangodb/internal/serde/SerdeUtils.java +++ b/core/src/main/java/com/arangodb/internal/serde/SerdeUtils.java @@ -65,7 +65,7 @@ public JsonNode parseJson(final String json) { try { return jsonMapper.readTree(json); } catch (JsonProcessingException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } @@ -77,7 +77,7 @@ public String writeJson(final JsonNode data) { try { return jsonMapper.writeValueAsString(data); } catch (JsonProcessingException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } diff --git a/core/src/main/java/com/arangodb/internal/util/EncodeUtils.java b/core/src/main/java/com/arangodb/internal/util/EncodeUtils.java index 1472b4301..0dde7d86a 100644 --- a/core/src/main/java/com/arangodb/internal/util/EncodeUtils.java +++ b/core/src/main/java/com/arangodb/internal/util/EncodeUtils.java @@ -48,7 +48,7 @@ public static String encodeURIComponent(final String value) { .replace("%29", ")") .replace("%7E", "~"); } catch (UnsupportedEncodingException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } diff --git a/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java b/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java index cd9049e3c..61b5b88df 100644 --- a/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java +++ b/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java @@ -53,7 +53,7 @@ public static void checkError(final InternalSerde util, final InternalResponse r final ErrorEntity errorEntity = util.deserialize(response.getBody(), ErrorEntity.class); ArangoDBException e = new ArangoDBException(errorEntity); if (ArangoErrors.QUEUE_TIME_VIOLATED.equals(e.getErrorNum())) { - throw new ArangoDBException(new TimeoutException().initCause(e)); + throw ArangoDBException.wrap(new TimeoutException().initCause(e)); } throw e; } else { @@ -72,7 +72,7 @@ public static ArangoDBException translateError(final InternalSerde util, final I final ErrorEntity errorEntity = util.deserialize(response.getBody(), ErrorEntity.class); ArangoDBException e = new ArangoDBException(errorEntity); if (ArangoErrors.QUEUE_TIME_VIOLATED.equals(e.getErrorNum())) { - return new ArangoDBException(new TimeoutException().initCause(e)); + return ArangoDBException.wrap(new TimeoutException().initCause(e)); } return e; } else { diff --git a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java index 8f6034afb..4bf36d6cf 100644 --- a/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoVertexCollectionAsyncTest.java @@ -115,12 +115,11 @@ void insertVertexViolatingUniqueConstraint(ArangoVertexCollectionAsync vertices) VertexEntity inserted = vertices.insertVertex(RawJson.of("{\"field\": 99}")).get(); - try { - vertices.insertVertex(RawJson.of("{\"field\": 99}")).get(); - } catch (ArangoDBException e) { - assertThat(e.getResponseCode()).isEqualTo(409); - assertThat(e.getErrorNum()).isEqualTo(1210); - } + Throwable thrown = catchThrowable(() -> vertices.insertVertex(RawJson.of("{\"field\": 99}")).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1210); // revert vertices.deleteVertex(inserted.getKey()).get(); diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index beab69741..4151c1cc0 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.net.SocketTimeoutException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; @@ -146,7 +147,7 @@ private CompletableFuture executeAsync(final InternalRequest r private void mirror(CompletableFuture up, CompletableFuture down) { up.whenComplete((v, err) -> { if (err != null) { - down.completeExceptionally(err); + down.completeExceptionally(err instanceof CompletionException ? err.getCause() : err); } else { down.complete(v); } diff --git a/http/src/main/java/com/arangodb/http/HttpConnection.java b/http/src/main/java/com/arangodb/http/HttpConnection.java index 3bfd5b393..87f3261bf 100644 --- a/http/src/main/java/com/arangodb/http/HttpConnection.java +++ b/http/src/main/java/com/arangodb/http/HttpConnection.java @@ -134,7 +134,7 @@ private static String getUserAgent() { try { ctx = SSLContext.getDefault(); } catch (NoSuchAlgorithmException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } diff --git a/vst/src/main/java/com/arangodb/vst/VstCommunication.java b/vst/src/main/java/com/arangodb/vst/VstCommunication.java index e7606df9f..20df268d2 100644 --- a/vst/src/main/java/com/arangodb/vst/VstCommunication.java +++ b/vst/src/main/java/com/arangodb/vst/VstCommunication.java @@ -114,7 +114,7 @@ protected synchronized C connect(final HostHandle hostHandle, final AccessType a failedHost.getDescription(), host.getDescription())); } else { LOGGER.error(e.getMessage(), e); - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } } diff --git a/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java b/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java index 07850d95e..4155da2fb 100644 --- a/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java +++ b/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java @@ -39,6 +39,7 @@ import org.slf4j.LoggerFactory; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; /** @@ -88,7 +89,7 @@ protected CompletableFuture execute(final InternalRequest requ if (v != null) { rfuture.complete(v); } else if (err != null) { - rfuture.completeExceptionally(err); + rfuture.completeExceptionally(err instanceof CompletionException ? err.getCause() : err); } else { rfuture.cancel(true); } @@ -99,8 +100,9 @@ protected CompletableFuture execute(final InternalRequest requ } rfuture.complete(response); } else if (ex != null) { - LOGGER.error(ex.getMessage(), ex); - rfuture.completeExceptionally(ex); + Throwable e = ex instanceof CompletionException ? ex.getCause() : ex; + LOGGER.error(e.getMessage(), e); + rfuture.completeExceptionally(e); } else { rfuture.cancel(true); } @@ -132,13 +134,11 @@ protected void authenticate(final VstConnectionAsync connection) { InternalResponse response; try { response = execute(authRequest, connection).get(); - } catch (final InterruptedException | ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof ArangoDBException) { - throw (ArangoDBException) cause; - } else { - throw new ArangoDBException(e.getCause()); - } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw ArangoDBException.wrap(e); + } catch (ExecutionException e) { + throw ArangoDBException.wrap(e.getCause()); } checkError(response); } diff --git a/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java b/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java index 7e8417205..d995e6e19 100644 --- a/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java +++ b/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java @@ -64,7 +64,7 @@ public Message get(final long messageId) { if (result == null) { final Exception e = error.remove(messageId); if (e != null) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } return result; diff --git a/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java b/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java index 59ce03e0c..e5f17172d 100644 --- a/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java +++ b/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java @@ -232,7 +232,7 @@ public synchronized void close() { } socket.close(); } catch (final IOException e) { - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } } @@ -271,7 +271,7 @@ protected synchronized void writeIntern(final Message message, final Collection< outputStream.flush(); } catch (final IOException e) { LOGGER.error("Error on Connection " + connectionName); - throw new ArangoDBException(e); + throw ArangoDBException.wrap(e); } } } From 1b1ed856dc277ab6450975f1a5beddb06bce47a5 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 11:54:00 +0200 Subject: [PATCH 39/62] test fixes --- .../src/test/java/com/arangodb/ArangoGraphAsyncTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java index d3dee7cac..f194d36d2 100644 --- a/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java @@ -181,8 +181,8 @@ void addSatelliteVertexCollection(ArangoDatabaseAsync db) throws ExecutionExcept String v1Name = "vertex-" + rnd(); ArangoGraphAsync g = db.graph(GRAPH_NAME + rnd()); - g.create(null, new GraphCreateOptions().isSmart(true).smartGraphAttribute("test")); - g.addVertexCollection(v1Name, new VertexCollectionCreateOptions().satellites(v1Name)); + g.create(null, new GraphCreateOptions().isSmart(true).smartGraphAttribute("test")).get(); + g.addVertexCollection(v1Name, new VertexCollectionCreateOptions().satellites(v1Name)).get(); Collection vertexCollections = g.getVertexCollections().get(); assertThat(vertexCollections).contains(v1Name); @@ -245,8 +245,8 @@ void addSatelliteEdgeDefinition(ArangoDatabaseAsync db) throws ExecutionExceptio EdgeDefinition ed = new EdgeDefinition().collection(eName).from(v1Name).to(v2Name).satellites(v1Name); ArangoGraphAsync g = db.graph(GRAPH_NAME + rnd()); - g.create(null, new GraphCreateOptions().isSmart(true).smartGraphAttribute("test")); - g.addEdgeDefinition(ed); + g.create(null, new GraphCreateOptions().isSmart(true).smartGraphAttribute("test")).get(); + g.addEdgeDefinition(ed).get(); final GraphEntity ge = g.getInfo().get(); assertThat(ge).isNotNull(); final Collection edgeDefinitions = ge.getEdgeDefinitions(); From 93a2292c51b8e64fd022b7d0bd41d0bbf050cddc Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 11:54:34 +0200 Subject: [PATCH 40/62] temporary disable acquireHostList --- driver/src/test/resources/arangodb-with-prefix.properties | 2 +- driver/src/test/resources/arangodb.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/driver/src/test/resources/arangodb-with-prefix.properties b/driver/src/test/resources/arangodb-with-prefix.properties index 36fb2d0a5..79ce26e82 100644 --- a/driver/src/test/resources/arangodb-with-prefix.properties +++ b/driver/src/test/resources/arangodb-with-prefix.properties @@ -1,3 +1,3 @@ adb.hosts=172.28.0.1:8529 -adb.acquireHostList=true +adb.acquireHostList=false adb.password=test diff --git a/driver/src/test/resources/arangodb.properties b/driver/src/test/resources/arangodb.properties index fa580e439..9875d233d 100644 --- a/driver/src/test/resources/arangodb.properties +++ b/driver/src/test/resources/arangodb.properties @@ -1,5 +1,5 @@ arangodb.hosts=172.28.0.1:8529 -arangodb.acquireHostList=true +arangodb.acquireHostList=false arangodb.password=test arangodb.timeout=30000 arangodb.responseQueueTimeSamples=20 From 77fda3db3e8e8892ba6a51d107b8918e232790af Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 12:15:03 +0200 Subject: [PATCH 41/62] CI upd --- .github/workflows/native.yml | 4 ++-- .github/workflows/test.yml | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml index c89f3e763..befd6747a 100644 --- a/.github/workflows/native.yml +++ b/.github/workflows/native.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: docker-img: - - docker.io/arangodb/enterprise:3.11.3 + - docker.io/arangodb/enterprise:3.11.4 topology: - single java-version: @@ -53,7 +53,7 @@ jobs: fail-fast: false matrix: docker-img: - - docker.io/arangodb/enterprise:3.11.3 + - docker.io/arangodb/enterprise:3.11.4 topology: - single java-version: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 92b69628e..7df277658 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,7 @@ on: push: branches: - main + - devel paths-ignore: - 'docker/**' - 'tutorial/**' @@ -14,6 +15,7 @@ on: types: [ opened, synchronize, reopened ] branches: - main + - devel jobs: @@ -26,9 +28,9 @@ jobs: matrix: docker-img: - docker.io/arangodb/arangodb:3.10.10 - - docker.io/arangodb/arangodb:3.11.3 + - docker.io/arangodb/arangodb:3.11.4 - docker.io/arangodb/enterprise:3.10.10 - - docker.io/arangodb/enterprise:3.11.3 + - docker.io/arangodb/enterprise:3.11.4 topology: - single - cluster @@ -68,7 +70,7 @@ jobs: fail-fast: false matrix: docker-img: - - docker.io/arangodb/arangodb:3.11.3 + - docker.io/arangodb/arangodb:3.11.4 topology: - single java-version: @@ -118,7 +120,7 @@ jobs: fail-fast: false matrix: docker-img: - - docker.io/arangodb/enterprise:3.11.3 + - docker.io/arangodb/enterprise:3.11.4 topology: - single - cluster @@ -172,7 +174,7 @@ jobs: - 2.11.4 - 2.10.5 docker-img: - - docker.io/arangodb/arangodb:3.11.3 + - docker.io/arangodb/arangodb:3.11.4 topology: - single db-ext-names: @@ -210,7 +212,7 @@ jobs: fail-fast: false matrix: docker-img: - - docker.io/arangodb/arangodb:3.11.3 + - docker.io/arangodb/arangodb:3.11.4 topology: - single - cluster @@ -255,7 +257,7 @@ jobs: fail-fast: false matrix: docker-img: - - docker.io/arangodb/enterprise:3.11.3 + - docker.io/arangodb/enterprise:3.11.4 topology: - cluster db-ext-names: From 04af5d2511e4e8403622722c7d30a6cba21387ec Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 12:37:03 +0200 Subject: [PATCH 42/62] test fixes --- .../test/java/com/arangodb/ArangoCollectionAsyncTest.java | 8 ++++---- .../test/java/com/arangodb/ArangoDatabaseAsyncTest.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java index 37baa6ef0..1ba37daa7 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java @@ -159,7 +159,7 @@ void insertDocumentWithTypeOverwriteModeReplace(ArangoCollectionAsync collection .returnNew(true) .returnOld(true) .overwriteMode(OverwriteMode.replace); - collection.insertDocument(dog, options); + collection.insertDocument(dog, options).get(); final DocumentCreateEntity doc = collection.insertDocument(cat, options, Animal.class).get(); assertThat(doc).isNotNull(); assertThat(doc.getId()).isNotNull(); @@ -284,7 +284,7 @@ void insertDocumentOverwriteModeUpdateKeepNullTrue(ArangoCollectionAsync collect final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); doc.addAttribute("foo", "bar"); - collection.insertDocument(doc); + collection.insertDocument(doc).get(); doc.updateAttribute("foo", null); final BaseDocument updated = collection.insertDocument(doc, new DocumentCreateOptions() @@ -541,7 +541,7 @@ void getDocuments(ArangoCollectionAsync collection) throws ExecutionException, I @MethodSource("asyncCols") void getDocumentsWithCustomShardingKey(ArangoCollectionAsync c) throws ExecutionException, InterruptedException { ArangoCollectionAsync collection = c.db().collection("customShardingKeyCollection"); - if (collection.exists().get()) collection.drop(); + if (collection.exists().get()) collection.drop().get(); collection.create(new CollectionCreateOptions().shardKeys("customField").numberOfShards(10)).get(); @@ -635,7 +635,7 @@ void updateDocumentWithDifferentReturnType(ArangoCollectionAsync collection) thr final String key = "key-" + UUID.randomUUID(); final BaseDocument doc = new BaseDocument(key); doc.addAttribute("a", "test"); - collection.insertDocument(doc); + collection.insertDocument(doc).get(); final DocumentUpdateEntity updateResult = collection.updateDocument(key, Collections.singletonMap("b", "test"), new DocumentUpdateOptions().returnNew(true), BaseDocument.class).get(); diff --git a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java index bbf8857dd..aed1d22ae 100644 --- a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java @@ -195,7 +195,7 @@ void createCollectionWithSmartJoinAttribute(ArangoDatabaseAsync db) throws Execu assumeTrue(isCluster()); String fooName = rndName(); - db.collection(fooName).create(); + db.collection(fooName).create().get(); String name = rndName(); final CollectionEntity result = db.createCollection(name, From 957cb729f3b758d6702173093cf9a56bb90b9e3d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 13:42:40 +0200 Subject: [PATCH 43/62] test fixes --- .../arangodb/ArangoCollectionAsyncTest.java | 26 +++++++++---------- .../com/arangodb/ArangoDatabaseAsyncTest.java | 2 +- .../com/arangodb/ArangoGraphAsyncTest.java | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java index 1ba37daa7..ea7e36e19 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java @@ -280,19 +280,19 @@ void insertDocumentOverwriteModeUpdateMergeObjectsFalse(ArangoCollectionAsync co @ParameterizedTest(name = "{index}") @MethodSource("asyncCols") void insertDocumentOverwriteModeUpdateKeepNullTrue(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { - assumeTrue(isAtLeastVersion(3, 7)); + assumeTrue(isAtLeastVersion(3, 7)); - final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); - doc.addAttribute("foo", "bar"); - collection.insertDocument(doc).get(); + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("foo", "bar"); + collection.insertDocument(doc).get(); - doc.updateAttribute("foo", null); - final BaseDocument updated = collection.insertDocument(doc, new DocumentCreateOptions() - .overwriteMode(OverwriteMode.update) - .keepNull(true) - .returnNew(true)).get().getNew(); + doc.updateAttribute("foo", null); + final BaseDocument updated = collection.insertDocument(doc, new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .keepNull(true) + .returnNew(true)).get().getNew(); - assertThat(updated.getProperties()).containsEntry("foo", null); + assertThat(updated.getProperties()).containsEntry("foo", null); } @ParameterizedTest(name = "{index}") @@ -1774,7 +1774,7 @@ void createTtlIndexWithOptions(ArangoCollectionAsync collection) throws Executio assertThat(indexResult.getName()).isEqualTo(name); // revert changes - collection.deleteIndex(indexResult.getId()); + collection.deleteIndex(indexResult.getId()).get(); } @ParameterizedTest(name = "{index}") @@ -2922,11 +2922,11 @@ void rename(ArangoCollectionAsync collection) throws ExecutionException, Interru ArangoDatabaseAsync db = collection.db(); if (!db.collection("c1").exists().get()) { - db.collection("c1").create(); + db.collection("c1").create().get(); } if (db.collection("c2").exists().get()) { - db.collection("c2").drop(); + db.collection("c2").drop().get(); } final CollectionEntity result = db.collection("c1").rename("c2").get(); diff --git a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java index aed1d22ae..8c4a32572 100644 --- a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java @@ -734,7 +734,7 @@ void changeQueryCache(ArangoDatabaseAsync db) throws ExecutionException, Interru final QueryCachePropertiesEntity properties2 = new QueryCachePropertiesEntity(); properties2.setMode(CacheMode.off); - db.setQueryCacheProperties(properties2); + db.setQueryCacheProperties(properties2).get(); } // @ParameterizedTest(name = "{index}") diff --git a/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java index f194d36d2..f99fc0ada 100644 --- a/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoGraphAsyncTest.java @@ -109,7 +109,7 @@ void createWithReplicationAndWriteConcern(ArangoDatabaseAsync db) throws Executi assertThat(graph.getName()).isEqualTo(GRAPH_NAME + "_1"); assertThat(graph.getWriteConcern()).isEqualTo(2); assertThat(graph.getReplicationFactor().get()).isEqualTo(2); - db.graph(GRAPH_NAME + "_1").drop(); + db.graph(GRAPH_NAME + "_1").drop().get(); } @ParameterizedTest(name = "{index}") From 490b1bec5decffbc0349881436f076ba99347d1a Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 13:46:28 +0200 Subject: [PATCH 44/62] temporary work-around for active failover tests --- driver/src/test/resources/arangodb.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/src/test/resources/arangodb.properties b/driver/src/test/resources/arangodb.properties index 9875d233d..9941cd11a 100644 --- a/driver/src/test/resources/arangodb.properties +++ b/driver/src/test/resources/arangodb.properties @@ -1,4 +1,4 @@ -arangodb.hosts=172.28.0.1:8529 +arangodb.hosts=172.28.0.1:8529,172.28.0.1:8539,172.28.0.1:8549 arangodb.acquireHostList=false arangodb.password=test arangodb.timeout=30000 From e4462ab078d99eb18e81db47ef8448d1a48c458c Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 15:35:22 +0200 Subject: [PATCH 45/62] fix jwt authentication --- .../com/arangodb/internal/net/ConnectionPoolImpl.java | 8 +++++--- core/src/main/java/com/arangodb/internal/net/HostSet.java | 4 ---- .../test/java/com/arangodb/ArangoCollectionAsyncTest.java | 2 +- driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java | 2 +- .../test/java/com/arangodb/internal/HostHandlerTest.java | 5 +++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/arangodb/internal/net/ConnectionPoolImpl.java b/core/src/main/java/com/arangodb/internal/net/ConnectionPoolImpl.java index 0de7d3474..57bfa5656 100644 --- a/core/src/main/java/com/arangodb/internal/net/ConnectionPoolImpl.java +++ b/core/src/main/java/com/arangodb/internal/net/ConnectionPoolImpl.java @@ -80,9 +80,11 @@ public synchronized Connection connection() { @Override public void setJwt(String jwt) { - this.jwt = jwt; - for (Connection connection : connections) { - connection.setJwt(jwt); + if (jwt != null) { + this.jwt = jwt; + for (Connection connection : connections) { + connection.setJwt(jwt); + } } } diff --git a/core/src/main/java/com/arangodb/internal/net/HostSet.java b/core/src/main/java/com/arangodb/internal/net/HostSet.java index 39c8d784f..d47612efc 100644 --- a/core/src/main/java/com/arangodb/internal/net/HostSet.java +++ b/core/src/main/java/com/arangodb/internal/net/HostSet.java @@ -15,10 +15,6 @@ public class HostSet { private final ArrayList hosts = new ArrayList<>(); private volatile String jwt = null; - public HostSet() { - super(); - } - public HostSet(List hosts) { super(); diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java index ea7e36e19..d2842a28d 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java @@ -566,7 +566,7 @@ void getDocumentsDirtyRead(ArangoCollectionAsync collection) throws ExecutionExc values.add(new BaseDocument("1")); values.add(new BaseDocument("2")); values.add(new BaseDocument("3")); - collection.insertDocuments(values); + collection.insertDocuments(values).get(); final MultiDocumentEntity documents = collection.getDocuments(Arrays.asList("1", "2", "3"), BaseDocument.class, new DocumentReadOptions().allowDirtyRead(true)).get(); assertThat(documents).isNotNull(); diff --git a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java index 1b941e7f8..6de65b0bb 100644 --- a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java @@ -532,7 +532,7 @@ void setAllLogLevel(ArangoDBAsync arangoDB) throws ExecutionException, Interrupt assertThat(retrievedLevels.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); } finally { entity.setAll(LogLevelEntity.LogLevel.INFO); - arangoDB.setLogLevel(entity); + arangoDB.setLogLevel(entity).get(); } } diff --git a/driver/src/test/java/com/arangodb/internal/HostHandlerTest.java b/driver/src/test/java/com/arangodb/internal/HostHandlerTest.java index 973d3a7c1..3803a3c4e 100644 --- a/driver/src/test/java/com/arangodb/internal/HostHandlerTest.java +++ b/driver/src/test/java/com/arangodb/internal/HostHandlerTest.java @@ -27,6 +27,7 @@ import com.arangodb.internal.serde.InternalSerde; import org.junit.jupiter.api.Test; +import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -65,7 +66,7 @@ public void close() { @Override public HostSet resolve(final boolean initial, final boolean closeConnections) { - HostSet set = new HostSet(); + HostSet set = new HostSet(Collections.emptyList()); set.addHost(HOST_0); return set; } @@ -83,7 +84,7 @@ public void init(ArangoExecutorSync executor, InternalSerde arangoSerialization) @Override public HostSet resolve(final boolean initial, final boolean closeConnections) { - HostSet set = new HostSet(); + HostSet set = new HostSet(Collections.emptyList()); set.addHost(HOST_0); set.addHost(HOST_1); set.addHost(HOST_2); From 7e6ba2e60150051ff2ea3a8e457e162104d39fee Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 18 Oct 2023 21:03:33 +0200 Subject: [PATCH 46/62] fix ConnectException handling --- .../com/arangodb/http/HttpCommunication.java | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index 4151c1cc0..66d52b05d 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -39,6 +39,7 @@ import java.io.Closeable; import java.io.IOException; +import java.net.ConnectException; import java.net.SocketTimeoutException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -90,27 +91,10 @@ private CompletableFuture executeAsync(final InternalRequest r rfuture.completeExceptionally(new ArangoDBException(te, reqId)); } else if (e instanceof TimeoutException) { rfuture.completeExceptionally(new ArangoDBException(e, reqId)); + } else if (e instanceof ConnectException) { + handleException(true, e, hostHandle, request, host, reqId, attemptCount, rfuture); } else if (e != null) { - IOException ioEx = wrapIOEx(e); - hostHandler.fail(ioEx); - if (hostHandle != null && hostHandle.getHost() != null) { - hostHandle.setHost(null); - } - - Host nextHost = hostHandler.get(hostHandle, RequestUtils.determineAccessType(request)); - if (nextHost != null && isSafe(request)) { - LOGGER.warn("Could not connect to {} while executing request [id={}]", - host.getDescription(), reqId, ioEx); - LOGGER.debug("Try connecting to {}", nextHost.getDescription()); - mirror( - executeAsync(request, hostHandle, nextHost, attemptCount), - rfuture - ); - } else { - ArangoDBException aEx = new ArangoDBException(ioEx, reqId); - LOGGER.error(aEx.getMessage(), aEx); - rfuture.completeExceptionally(aEx); - } + handleException(isSafe(request), e, hostHandle, request, host, reqId, attemptCount, rfuture); } else { if (LOGGER.isDebugEnabled()) { String body = response.getBody() == null ? "" : serde.toJsonString(response.getBody()); @@ -144,6 +128,29 @@ private CompletableFuture executeAsync(final InternalRequest r return rfuture; } + private void handleException(boolean isSafe, Throwable e, HostHandle hostHandle, InternalRequest request, Host host, + long reqId, int attemptCount, CompletableFuture rfuture) { + IOException ioEx = wrapIOEx(e); + hostHandler.fail(ioEx); + if (hostHandle != null && hostHandle.getHost() != null) { + hostHandle.setHost(null); + } + Host nextHost = hostHandler.get(hostHandle, RequestUtils.determineAccessType(request)); + if (nextHost != null && isSafe) { + LOGGER.warn("Could not connect to {} while executing request [id={}]", + host.getDescription(), reqId, ioEx); + LOGGER.debug("Try connecting to {}", nextHost.getDescription()); + mirror( + executeAsync(request, hostHandle, nextHost, attemptCount), + rfuture + ); + } else { + ArangoDBException aEx = new ArangoDBException(ioEx, reqId); + LOGGER.error(aEx.getMessage(), aEx); + rfuture.completeExceptionally(aEx); + } + } + private void mirror(CompletableFuture up, CompletableFuture down) { up.whenComplete((v, err) -> { if (err != null) { From ea4f72d70aaa00d9fcfaedd618ab8da725647cf0 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 19 Oct 2023 09:03:37 +0200 Subject: [PATCH 47/62] test fixes --- .../arangodb/ArangoCollectionAsyncTest.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java index d2842a28d..63f5e2efa 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java @@ -280,19 +280,19 @@ void insertDocumentOverwriteModeUpdateMergeObjectsFalse(ArangoCollectionAsync co @ParameterizedTest(name = "{index}") @MethodSource("asyncCols") void insertDocumentOverwriteModeUpdateKeepNullTrue(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { - assumeTrue(isAtLeastVersion(3, 7)); + assumeTrue(isAtLeastVersion(3, 7)); - final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); - doc.addAttribute("foo", "bar"); - collection.insertDocument(doc).get(); + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("foo", "bar"); + collection.insertDocument(doc).get(); - doc.updateAttribute("foo", null); - final BaseDocument updated = collection.insertDocument(doc, new DocumentCreateOptions() - .overwriteMode(OverwriteMode.update) - .keepNull(true) - .returnNew(true)).get().getNew(); + doc.updateAttribute("foo", null); + final BaseDocument updated = collection.insertDocument(doc, new DocumentCreateOptions() + .overwriteMode(OverwriteMode.update) + .keepNull(true) + .returnNew(true)).get().getNew(); - assertThat(updated.getProperties()).containsEntry("foo", null); + assertThat(updated.getProperties()).containsEntry("foo", null); } @ParameterizedTest(name = "{index}") @@ -1593,7 +1593,7 @@ void createZKDIndex(ArangoCollectionAsync collection) throws ExecutionException, @MethodSource("asyncCols") void createZKDIndexWithOptions(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { assumeTrue(isAtLeastVersion(3, 9)); - collection.truncate(); + collection.truncate().get(); String name = "ZKDIndex-" + rnd(); final ZKDIndexOptions options = @@ -1614,7 +1614,7 @@ void createZKDIndexWithOptions(ArangoCollectionAsync collection) throws Executio assertThat(indexResult.getType()).isEqualTo(IndexType.zkd); assertThat(indexResult.getUnique()).isFalse(); assertThat(indexResult.getName()).isEqualTo(name); - collection.deleteIndex(indexResult.getId()); + collection.deleteIndex(indexResult.getId()).get(); } @ParameterizedTest(name = "{index}") @@ -2845,7 +2845,7 @@ void replaceDocumentsRawData(ArangoCollectionAsync collection) throws ExecutionE @MethodSource("asyncCols") void replaceDocumentsRawDataReturnNew(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { final RawData values = RawJson.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); - collection.insertDocuments(values); + collection.insertDocuments(values).get(); final RawData updatedValues = RawJson.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + "\"foo\":\"bar\"}]"); From 48f06fe4b997d68faa53593ec3cf9983b61564d2 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 19 Oct 2023 12:27:27 +0200 Subject: [PATCH 48/62] native test fixes --- driver/pom.xml | 2 +- .../native-image/native-image.properties | 1 + .../META-INF/native-image/reflect-config.json | 54 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/driver/pom.xml b/driver/pom.xml index 5a4156c4d..52e97a3b2 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -187,7 +187,7 @@ org.graalvm.sdk graal-sdk 22.3.3 - provided + test io.smallrye.config diff --git a/driver/src/test/resources/META-INF/native-image/native-image.properties b/driver/src/test/resources/META-INF/native-image/native-image.properties index 7a9968e8f..98ae5b0b1 100644 --- a/driver/src/test/resources/META-INF/native-image/native-image.properties +++ b/driver/src/test/resources/META-INF/native-image/native-image.properties @@ -1,4 +1,5 @@ Args=\ + -H:+AllowDeprecatedBuilderClassesOnImageClasspath \ -H:ResourceConfigurationResources=${.}/resource-config.json \ -H:ReflectionConfigurationResources=${.}/reflect-config.json \ -H:SerializationConfigurationResources=${.}/serialization-config.json \ diff --git a/driver/src/test/resources/META-INF/native-image/reflect-config.json b/driver/src/test/resources/META-INF/native-image/reflect-config.json index db03957e0..9e4087f00 100644 --- a/driver/src/test/resources/META-INF/native-image/reflect-config.json +++ b/driver/src/test/resources/META-INF/native-image/reflect-config.json @@ -225,5 +225,59 @@ "allPublicMethods": true, "allDeclaredConstructors": true, "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionAsyncTest$TestUpdateEntity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionAsyncTest$TestUpdateEntitySerializeNullFalse", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoDatabaseAsyncTest$TransactionTestEntity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionAsyncTest$Animal", + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionAsyncTest$AnnotatedEntity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionAsyncTest$Cat", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.ArangoCollectionAsyncTest$Dog", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredConstructors": true, + "allDeclaredClasses": true } ] From 01b63b32a0ab053107f5e5503c9bf993193851c5 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 19 Oct 2023 14:42:34 +0200 Subject: [PATCH 49/62] ArangoDBException factory methods refactoring --- .../java/com/arangodb/ArangoDBException.java | 79 +++++++++++++++---- .../internal/ArangoCollectionAsyncImpl.java | 4 +- .../internal/ArangoDatabaseAsyncImpl.java | 2 +- .../com/arangodb/internal/ArangoExecutor.java | 2 +- .../internal/ArangoExecutorAsync.java | 2 +- .../internal/ArangoGraphAsyncImpl.java | 2 +- .../internal/ArangoSearchAsyncImpl.java | 2 +- .../internal/ArangoViewAsyncImpl.java | 2 +- .../internal/SearchAliasAsyncImpl.java | 2 +- .../config/ArangoConfigPropertiesImpl.java | 2 +- .../internal/net/CommunicationProtocol.java | 4 +- .../internal/net/FallbackHostHandler.java | 2 +- .../com/arangodb/internal/net/HostImpl.java | 2 +- .../internal/net/RoundRobinHostHandler.java | 2 +- .../internal/serde/InternalSerdeImpl.java | 14 ++-- .../arangodb/internal/serde/SerdeUtils.java | 4 +- .../arangodb/internal/util/EncodeUtils.java | 2 +- .../arangodb/internal/util/ResponseUtils.java | 4 +- .../com/arangodb/http/HttpCommunication.java | 6 +- .../com/arangodb/http/HttpConnection.java | 2 +- .../com/arangodb/vst/VstCommunication.java | 2 +- .../arangodb/vst/VstCommunicationAsync.java | 4 +- .../arangodb/vst/internal/MessageStore.java | 2 +- .../arangodb/vst/internal/VstConnection.java | 4 +- 24 files changed, 101 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/com/arangodb/ArangoDBException.java b/core/src/main/java/com/arangodb/ArangoDBException.java index 2f0ebfdae..9bc9a47fe 100644 --- a/core/src/main/java/com/arangodb/ArangoDBException.java +++ b/core/src/main/java/com/arangodb/ArangoDBException.java @@ -22,6 +22,7 @@ import com.arangodb.entity.ErrorEntity; +import java.util.Objects; import java.util.concurrent.CompletionException; /** @@ -56,6 +57,10 @@ public ArangoDBException(final String message, final Integer responseCode) { this.requestId = null; } + /** + * @deprecated use {@link com.arangodb.ArangoDBException#of(java.lang.Throwable)} instead + */ + @Deprecated public ArangoDBException(final Throwable cause) { super(cause); this.entity = null; @@ -63,6 +68,10 @@ public ArangoDBException(final Throwable cause) { this.requestId = null; } + /** + * @deprecated use {@link com.arangodb.ArangoDBException#of(String, Throwable)} instead + */ + @Deprecated public ArangoDBException(final String message, final Throwable cause) { super(message, cause); this.entity = null; @@ -70,6 +79,10 @@ public ArangoDBException(final String message, final Throwable cause) { this.requestId = null; } + /** + * @deprecated use {@link com.arangodb.ArangoDBException#of(Throwable, Long)} instead + */ + @Deprecated public ArangoDBException(Throwable cause, long requestId) { super(cause); this.entity = null; @@ -77,25 +90,61 @@ public ArangoDBException(Throwable cause, long requestId) { this.requestId = requestId; } - private ArangoDBException(final ArangoDBException e) { - super(e.getMessage(), e); - this.entity = e.entity; - this.responseCode = e.responseCode; - this.requestId = e.requestId; + private ArangoDBException( + String message, + Throwable cause, + ErrorEntity entity, + Integer responseCode, + Long requestId + ) { + super(message, cause); + this.entity = entity; + this.responseCode = responseCode; + this.requestId = requestId; + } + + public static ArangoDBException of(Throwable t) { + return of(null, t); + } + + public static ArangoDBException of(String message, Throwable t) { + return of(message, t, null); + } + + public static ArangoDBException of(Throwable t, Long requestId) { + return of(null, t, requestId); } - public static ArangoDBException wrap(Throwable t) { + private static ArangoDBException of(String message, Throwable t, Long requestId) { + Objects.requireNonNull(t); + Throwable cause = unwrapCause(t); + String msg = message != null ? message + : t.getMessage() != null ? t.getMessage() + : cause.getMessage(); + ErrorEntity entity = null; + Integer responseCode = null; + Long reqId = requestId; + if (t instanceof ArangoDBException) { - if (t.getCause() == null) { - return new ArangoDBException((ArangoDBException) t); - } else { - return wrap(t.getCause()); - } - } else if (t instanceof CompletionException) { - return wrap(t.getCause()); - } else { - return new ArangoDBException(t); + entity = ((ArangoDBException) t).entity; + responseCode = ((ArangoDBException) t).responseCode; + reqId = reqId != null ? reqId : ((ArangoDBException) t).requestId; + } + + return new ArangoDBException( + msg, + cause, + entity, + responseCode, + reqId + ); + } + + private static Throwable unwrapCause(Throwable t) { + if (t instanceof ArangoDBException || t instanceof CompletionException) { + return unwrapCause(t.getCause()); } + return t; } /** diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java index 222b9e696..a3a6de171 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java @@ -334,7 +334,7 @@ T catchGetDocumentExceptions(Throwable err) { return null; } } - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } @Override @@ -405,7 +405,7 @@ public CompletableFuture exists() { return false; } } - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index bb49e0f08..fcd404a15 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -79,7 +79,7 @@ public CompletableFuture exists() { } } - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecutor.java b/core/src/main/java/com/arangodb/internal/ArangoExecutor.java index bccc7880c..3f491f701 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecutor.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecutor.java @@ -50,7 +50,7 @@ public void disconnect() { try { protocol.close(); } catch (final IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java b/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java index 0aabc9ac3..2cd114bf6 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java @@ -58,7 +58,7 @@ public CompletableFuture execute( return protocol.executeAsync(interceptRequest(request), hostHandle) .handle((r, e) -> { if (e != null) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } else { interceptResponse(r); return responseDeserializer.deserialize(r); diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java index f6653ee6e..b70ace6e0 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java @@ -58,7 +58,7 @@ public CompletableFuture exists() { return false; } } - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java index ed89e13c7..1f7f3b8e9 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java @@ -58,7 +58,7 @@ public CompletableFuture exists() { return false; } } - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java index 17888e598..4e4643544 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java @@ -54,7 +54,7 @@ public CompletableFuture exists() { return false; } } - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java b/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java index 9928e2749..ef02daded 100644 --- a/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java @@ -58,7 +58,7 @@ public CompletableFuture exists() { return false; } } - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); }); } diff --git a/core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java b/core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java index 7484c6e23..df51a0a6b 100644 --- a/core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java +++ b/core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java @@ -48,7 +48,7 @@ private Properties initProperties(String fileName) { try (InputStream is = getClass().getClassLoader().getResourceAsStream(fileName)) { p.load(is); } catch (Exception e) { - throw new ArangoDBException("Got exception while reading properties file " + fileName, e); + throw ArangoDBException.of("Got exception while reading properties file " + fileName, e); } return p; } diff --git a/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java b/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java index e35f917cc..82fefdaa4 100644 --- a/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java +++ b/core/src/main/java/com/arangodb/internal/net/CommunicationProtocol.java @@ -38,9 +38,9 @@ default InternalResponse execute(final InternalRequest request, final HostHandle return executeAsync(request, hostHandle).get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } catch (ExecutionException e) { - throw ArangoDBException.wrap(e.getCause()); + throw ArangoDBException.of(e.getCause()); } } diff --git a/core/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java b/core/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java index 5ea722d6d..162adaf86 100644 --- a/core/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java +++ b/core/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java @@ -54,7 +54,7 @@ public Host get(final HostHandle hostHandle, AccessType accessType) { if (current != lastSuccess || iterations < 3) { return current; } else { - ArangoDBException e = new ArangoDBException("Cannot contact any host!", + ArangoDBException e = ArangoDBException.of("Cannot contact any host!", new ArangoDBMultipleException(new ArrayList<>(lastFailExceptions))); reset(); throw e; diff --git a/core/src/main/java/com/arangodb/internal/net/HostImpl.java b/core/src/main/java/com/arangodb/internal/net/HostImpl.java index f0dde825f..1ef822618 100644 --- a/core/src/main/java/com/arangodb/internal/net/HostImpl.java +++ b/core/src/main/java/com/arangodb/internal/net/HostImpl.java @@ -60,7 +60,7 @@ public void closeOnError() { try { connectionPool.close(); } catch (final IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } diff --git a/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java b/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java index d980ef5cc..23f5be99c 100644 --- a/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java +++ b/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java @@ -54,7 +54,7 @@ public Host get(final HostHandle hostHandle, AccessType accessType) { final int size = hosts.getHostsList().size(); if (fails > size) { - ArangoDBException e = new ArangoDBException("Cannot contact any host!", + ArangoDBException e = ArangoDBException.of("Cannot contact any host!", new ArangoDBMultipleException(new ArrayList<>(lastFailExceptions))); reset(); throw e; diff --git a/core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java b/core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java index 159f53ac5..7340f7073 100644 --- a/core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java +++ b/core/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java @@ -51,7 +51,7 @@ public byte[] serialize(final Object value) { try { return mapper.writeValueAsBytes(value); } catch (JsonProcessingException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } @@ -65,7 +65,7 @@ public String toJsonString(final byte[] content) { try { return SerdeUtils.INSTANCE.writeJson(mapper.readTree(content)); } catch (IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } @@ -75,7 +75,7 @@ public byte[] extract(final byte[] content, final String jsonPointer) { JsonNode target = parse(content).at(jsonPointer); return mapper.writeValueAsBytes(target); } catch (IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } @@ -84,7 +84,7 @@ public JsonNode parse(byte[] content) { try { return mapper.readTree(content); } catch (IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } @@ -93,7 +93,7 @@ public JsonNode parse(byte[] content, String jsonPointer) { try { return mapper.readTree(content).at(jsonPointer); } catch (IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } @@ -153,7 +153,7 @@ public T deserialize(final JsonNode node, final Type type) { try { return mapper.readerFor(mapper.constructType(type)).readValue(node); } catch (IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } @@ -165,7 +165,7 @@ public T deserialize(final byte[] content, final Type type) { try { return mapper.readerFor(mapper.constructType(type)).readValue(content); } catch (IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } diff --git a/core/src/main/java/com/arangodb/internal/serde/SerdeUtils.java b/core/src/main/java/com/arangodb/internal/serde/SerdeUtils.java index d9dac1da8..223b242a4 100644 --- a/core/src/main/java/com/arangodb/internal/serde/SerdeUtils.java +++ b/core/src/main/java/com/arangodb/internal/serde/SerdeUtils.java @@ -65,7 +65,7 @@ public JsonNode parseJson(final String json) { try { return jsonMapper.readTree(json); } catch (JsonProcessingException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } @@ -77,7 +77,7 @@ public String writeJson(final JsonNode data) { try { return jsonMapper.writeValueAsString(data); } catch (JsonProcessingException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } diff --git a/core/src/main/java/com/arangodb/internal/util/EncodeUtils.java b/core/src/main/java/com/arangodb/internal/util/EncodeUtils.java index 0dde7d86a..11740abf2 100644 --- a/core/src/main/java/com/arangodb/internal/util/EncodeUtils.java +++ b/core/src/main/java/com/arangodb/internal/util/EncodeUtils.java @@ -48,7 +48,7 @@ public static String encodeURIComponent(final String value) { .replace("%29", ")") .replace("%7E", "~"); } catch (UnsupportedEncodingException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } diff --git a/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java b/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java index 61b5b88df..eb48a2314 100644 --- a/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java +++ b/core/src/main/java/com/arangodb/internal/util/ResponseUtils.java @@ -53,7 +53,7 @@ public static void checkError(final InternalSerde util, final InternalResponse r final ErrorEntity errorEntity = util.deserialize(response.getBody(), ErrorEntity.class); ArangoDBException e = new ArangoDBException(errorEntity); if (ArangoErrors.QUEUE_TIME_VIOLATED.equals(e.getErrorNum())) { - throw ArangoDBException.wrap(new TimeoutException().initCause(e)); + throw ArangoDBException.of(new TimeoutException().initCause(e)); } throw e; } else { @@ -72,7 +72,7 @@ public static ArangoDBException translateError(final InternalSerde util, final I final ErrorEntity errorEntity = util.deserialize(response.getBody(), ErrorEntity.class); ArangoDBException e = new ArangoDBException(errorEntity); if (ArangoErrors.QUEUE_TIME_VIOLATED.equals(e.getErrorNum())) { - return ArangoDBException.wrap(new TimeoutException().initCause(e)); + return ArangoDBException.of(new TimeoutException().initCause(e)); } return e; } else { diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index 66d52b05d..c929d08c4 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -88,9 +88,9 @@ private CompletableFuture executeAsync(final InternalRequest r // SocketTimeoutException exceptions are wrapped and rethrown. TimeoutException te = new TimeoutException(e.getMessage()); te.initCause(e); - rfuture.completeExceptionally(new ArangoDBException(te, reqId)); + rfuture.completeExceptionally(ArangoDBException.of(te, reqId)); } else if (e instanceof TimeoutException) { - rfuture.completeExceptionally(new ArangoDBException(e, reqId)); + rfuture.completeExceptionally(ArangoDBException.of(e, reqId)); } else if (e instanceof ConnectException) { handleException(true, e, hostHandle, request, host, reqId, attemptCount, rfuture); } else if (e != null) { @@ -145,7 +145,7 @@ private void handleException(boolean isSafe, Throwable e, HostHandle hostHandle, rfuture ); } else { - ArangoDBException aEx = new ArangoDBException(ioEx, reqId); + ArangoDBException aEx = ArangoDBException.of(ioEx, reqId); LOGGER.error(aEx.getMessage(), aEx); rfuture.completeExceptionally(aEx); } diff --git a/http/src/main/java/com/arangodb/http/HttpConnection.java b/http/src/main/java/com/arangodb/http/HttpConnection.java index 87f3261bf..6d5833465 100644 --- a/http/src/main/java/com/arangodb/http/HttpConnection.java +++ b/http/src/main/java/com/arangodb/http/HttpConnection.java @@ -134,7 +134,7 @@ private static String getUserAgent() { try { ctx = SSLContext.getDefault(); } catch (NoSuchAlgorithmException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } diff --git a/vst/src/main/java/com/arangodb/vst/VstCommunication.java b/vst/src/main/java/com/arangodb/vst/VstCommunication.java index 20df268d2..847da7107 100644 --- a/vst/src/main/java/com/arangodb/vst/VstCommunication.java +++ b/vst/src/main/java/com/arangodb/vst/VstCommunication.java @@ -114,7 +114,7 @@ protected synchronized C connect(final HostHandle hostHandle, final AccessType a failedHost.getDescription(), host.getDescription())); } else { LOGGER.error(e.getMessage(), e); - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } } diff --git a/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java b/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java index 4155da2fb..8c0aa11e5 100644 --- a/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java +++ b/vst/src/main/java/com/arangodb/vst/VstCommunicationAsync.java @@ -136,9 +136,9 @@ protected void authenticate(final VstConnectionAsync connection) { response = execute(authRequest, connection).get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } catch (ExecutionException e) { - throw ArangoDBException.wrap(e.getCause()); + throw ArangoDBException.of(e.getCause()); } checkError(response); } diff --git a/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java b/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java index d995e6e19..707a3c325 100644 --- a/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java +++ b/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java @@ -64,7 +64,7 @@ public Message get(final long messageId) { if (result == null) { final Exception e = error.remove(messageId); if (e != null) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } return result; diff --git a/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java b/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java index e5f17172d..1cf653ae2 100644 --- a/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java +++ b/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java @@ -232,7 +232,7 @@ public synchronized void close() { } socket.close(); } catch (final IOException e) { - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } } @@ -271,7 +271,7 @@ protected synchronized void writeIntern(final Message message, final Collection< outputStream.flush(); } catch (final IOException e) { LOGGER.error("Error on Connection " + connectionName); - throw ArangoDBException.wrap(e); + throw ArangoDBException.of(e); } } } From a24d134ab9151abcf56cd2d43f4560c9882916ae Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 23 Oct 2023 15:31:33 +0200 Subject: [PATCH 50/62] [DE-497] Asynchronous AQL cursors (#520) * ArangoCursorAsync (wip) * ArangoDatabaseAsyncTest * async tests with query * ArangoCursorAsync#close() * ArangoCursorAsync.nextBatch() * test fixes * ArangoDatabaseAsync.cursor() * async tests parity * tests fixes * dbg tests failures * test fixes * test fixes --- .../java/com/arangodb/ArangoCursorAsync.java | 30 + .../com/arangodb/ArangoDatabaseAsync.java | 22 +- .../java/com/arangodb/BaseArangoCursor.java | 23 + .../com/arangodb/entity/CursorEntity.java | 128 +++ .../internal/ArangoDatabaseAsyncImpl.java | 107 +- .../arangodb/internal/ArangoDatabaseImpl.java | 13 +- .../arangodb/internal/ArangoExecuteable.java | 1 + .../internal/InternalArangoCursor.java | 111 ++ .../internal/InternalArangoDatabase.java | 23 +- .../cursor/ArangoCursorAsyncImpl.java | 50 + driver/src/test/java/CommunicationTest.java | 74 ++ .../java/com/arangodb/ArangoDBAsyncTest.java | 83 +- .../com/arangodb/ArangoDatabaseAsyncTest.java | 1001 ++++++++--------- .../com/arangodb/ArangoSearchAsyncTest.java | 62 +- .../com/arangodb/ConcurrencyAsyncTests.java | 92 ++ .../com/arangodb/InvertedIndexAsyncTest.java | 209 ++++ .../java/com/arangodb/JwtAuthAsyncTest.java | 101 ++ .../java/com/arangodb/ParallelAsyncTest.java | 39 + .../arangodb/StreamTransactionAsyncTest.java | 187 ++- .../java/com/arangodb/UserAgentAsyncTest.java | 43 + .../test/java/perf/SimpleAsyncPerfTest.java | 79 ++ 21 files changed, 1679 insertions(+), 799 deletions(-) create mode 100644 core/src/main/java/com/arangodb/ArangoCursorAsync.java create mode 100644 core/src/main/java/com/arangodb/BaseArangoCursor.java create mode 100644 core/src/main/java/com/arangodb/entity/CursorEntity.java create mode 100644 core/src/main/java/com/arangodb/internal/InternalArangoCursor.java create mode 100644 core/src/main/java/com/arangodb/internal/cursor/ArangoCursorAsyncImpl.java create mode 100644 driver/src/test/java/CommunicationTest.java create mode 100644 driver/src/test/java/com/arangodb/ConcurrencyAsyncTests.java create mode 100644 driver/src/test/java/com/arangodb/InvertedIndexAsyncTest.java create mode 100644 driver/src/test/java/com/arangodb/JwtAuthAsyncTest.java create mode 100644 driver/src/test/java/com/arangodb/ParallelAsyncTest.java create mode 100644 driver/src/test/java/com/arangodb/UserAgentAsyncTest.java create mode 100644 driver/src/test/java/perf/SimpleAsyncPerfTest.java diff --git a/core/src/main/java/com/arangodb/ArangoCursorAsync.java b/core/src/main/java/com/arangodb/ArangoCursorAsync.java new file mode 100644 index 000000000..e77bd6257 --- /dev/null +++ b/core/src/main/java/com/arangodb/ArangoCursorAsync.java @@ -0,0 +1,30 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import java.util.concurrent.CompletableFuture; + +public interface ArangoCursorAsync extends BaseArangoCursor { + + CompletableFuture> nextBatch(); + + CompletableFuture close(); +} diff --git a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java index 8b6f22052..e3b90b78b 100644 --- a/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java +++ b/core/src/main/java/com/arangodb/ArangoDatabaseAsync.java @@ -146,17 +146,17 @@ public interface ArangoDatabaseAsync extends ArangoSerdeAccessor { */ CompletableFuture getPermissions(String user); -// CompletableFuture> query(String query, Class type, Map bindVars, AqlQueryOptions options); -// -// CompletableFuture> query(String query, Class type, AqlQueryOptions options); -// -// CompletableFuture> query(String query, Class type, Map bindVars); -// -// CompletableFuture> query(String query, Class type); -// -// CompletableFuture> cursor(String cursorId, Class type); -// -// CompletableFuture> cursor(String cursorId, Class type, String nextBatchId); + CompletableFuture> query(String query, Class type, Map bindVars, AqlQueryOptions options); + + CompletableFuture> query(String query, Class type, AqlQueryOptions options); + + CompletableFuture> query(String query, Class type, Map bindVars); + + CompletableFuture> query(String query, Class type); + + CompletableFuture> cursor(String cursorId, Class type); + + CompletableFuture> cursor(String cursorId, Class type, String nextBatchId); /** * Asynchronous version of {@link ArangoDatabase#explainQuery(String, Map, AqlQueryExplainOptions)} diff --git a/core/src/main/java/com/arangodb/BaseArangoCursor.java b/core/src/main/java/com/arangodb/BaseArangoCursor.java new file mode 100644 index 000000000..c2f48860e --- /dev/null +++ b/core/src/main/java/com/arangodb/BaseArangoCursor.java @@ -0,0 +1,23 @@ +package com.arangodb; + +import com.arangodb.entity.CursorEntity; + +import java.util.List; + +public interface BaseArangoCursor { + String getId(); + + Long getCount(); + + Boolean isCached(); + + Boolean hasMore(); + + List getResult(); + + Boolean isPotentialDirtyRead(); + + String getNextBatchId(); + + CursorEntity.Extra getExtra(); +} diff --git a/core/src/main/java/com/arangodb/entity/CursorEntity.java b/core/src/main/java/com/arangodb/entity/CursorEntity.java new file mode 100644 index 000000000..b862563f2 --- /dev/null +++ b/core/src/main/java/com/arangodb/entity/CursorEntity.java @@ -0,0 +1,128 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.entity; + +import com.arangodb.internal.serde.UserDataInside; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * @author Mark Vollmary + * @see API + * Documentation + */ +public final class CursorEntity { + private String id; + private Long count; + private Boolean cached; + private Boolean hasMore; + + // TODO: test whether user-serde is used for result elements + @UserDataInside + private List result; + private Boolean potentialDirtyRead; + private String nextBatchId; + private final Extra extra = new Extra(); + + public String getId() { + return id; + } + + /** + * @return the total number of result documents available (only available if the query was executed with the count + * attribute set) + */ + public Long getCount() { + return count; + } + + /** + * @return an optional object with extra information about the query result contained in its stats sub-attribute. + * For data-modification queries, the extra.stats sub-attribute will contain the number of modified + * documents and the number of documents that could not be modified due to an error (if ignoreErrors query + * option is specified) + */ + public Extra getExtra() { + return extra; + } + + /** + * @return a boolean flag indicating whether the query result was served from the query cache or not. If the query + * result is served from the query cache, the extra return attribute will not contain any stats + * sub-attribute and no profile sub-attribute. + */ + public Boolean getCached() { + return cached; + } + + /** + * @return A boolean indicator whether there are more results available for the cursor on the server + */ + public Boolean getHasMore() { + return hasMore; + } + + /** + * @return a list of result documents (might be empty if query has no results) + */ + public List getResult() { + return result; + } + + /** + * @return true if the result is a potential dirty read + * @since ArangoDB 3.10 + */ + public Boolean isPotentialDirtyRead() { + return potentialDirtyRead; + } + + public void setPotentialDirtyRead(final Boolean potentialDirtyRead) { + this.potentialDirtyRead = potentialDirtyRead; + } + + /** + * @return The ID of the batch after the current one. The first batch has an ID of 1 and the value is incremented by + * 1 with every batch. Only set if the allowRetry query option is enabled. + * @since ArangoDB 3.11 + */ + public String getNextBatchId() { + return nextBatchId; + } + + public static final class Extra { + private final Collection warnings = Collections.emptyList(); + private CursorStats stats; + + public CursorStats getStats() { + return stats; + } + + public Collection getWarnings() { + return warnings; + } + + } + +} + diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index fcd404a15..eb3cac9d1 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -23,6 +23,8 @@ import com.arangodb.*; import com.arangodb.entity.*; import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; +import com.arangodb.internal.cursor.ArangoCursorAsyncImpl; +import com.arangodb.internal.net.HostHandle; import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; import com.arangodb.model.arangosearch.AnalyzerDeleteOptions; @@ -168,72 +170,45 @@ public CompletableFuture getPermissions(final String user) { return executorAsync().execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); } -// @Override -// public ArangoCursor query( -// final String query, final Class type, final Map bindVars, final AqlQueryOptions options) { -// final InternalRequest request = queryRequest(query, bindVars, options); -// final HostHandle hostHandle = new HostHandle(); -// final InternalCursorEntity result = executorAsync().execute(request, internalCursorEntityDeserializer(), hostHandle); -// return createCursor(result, type, options, hostHandle); -// } -// -// @Override -// public ArangoCursor query(final String query, final Class type, final Map bindVars) { -// return query(query, type, bindVars, new AqlQueryOptions()); -// } -// -// @Override -// public ArangoCursor query(final String query, final Class type, final AqlQueryOptions options) { -// return query(query, type, null, options); -// } -// -// @Override -// public ArangoCursor query(final String query, final Class type) { -// return query(query, type, null, new AqlQueryOptions()); -// } -// -// @Override -// public ArangoCursor cursor(final String cursorId, final Class type) { -// final HostHandle hostHandle = new HostHandle(); -// final InternalCursorEntity result = executorAsync().execute( -// queryNextRequest(cursorId, null), -// internalCursorEntityDeserializer(), -// hostHandle); -// return createCursor(result, type, null, hostHandle); -// } -// -// @Override -// public ArangoCursor cursor(final String cursorId, final Class type, final String nextBatchId) { -// final HostHandle hostHandle = new HostHandle(); -// final InternalCursorEntity result = executorAsync().execute( -// queryNextByBatchIdRequest(cursorId, nextBatchId, null), -// internalCursorEntityDeserializer(), -// hostHandle); -// return createCursor(result, type, null, hostHandle); -// } -// -// private ArangoCursor createCursor( -// final InternalCursorEntity result, -// final Class type, -// final AqlQueryOptions options, -// final HostHandle hostHandle) { -// -// final ArangoCursorExecute execute = new ArangoCursorExecute() { -// @Override -// public InternalCursorEntity next(final String id, final String nextBatchId) { -// InternalRequest request = nextBatchId == null ? -// queryNextRequest(id, options) : queryNextByBatchIdRequest(id, nextBatchId, options); -// return executorAsync().execute(request, internalCursorEntityDeserializer(), hostHandle); -// } -// -// @Override -// public void close(final String id) { -// executorAsync().execute(queryCloseRequest(id, options), Void.class, hostHandle); -// } -// }; -// -// return new ArangoCursorImpl<>(this, execute, type, result); -// } + @Override + public CompletableFuture> query( + final String query, final Class type, final Map bindVars, final AqlQueryOptions options) { + final InternalRequest request = queryRequest(query, bindVars, options); + final HostHandle hostHandle = new HostHandle(); + return executorAsync().execute(request, cursorEntityDeserializer(type), hostHandle) + .thenApply(res -> new ArangoCursorAsyncImpl<>(this, res, type, hostHandle, options.getAllowRetry())); + } + + @Override + public CompletableFuture> query(String query, Class type, AqlQueryOptions options) { + return query(query, type, null, options); + } + + @Override + public CompletableFuture> query(String query, Class type, Map bindVars) { + return query(query, type, bindVars, new AqlQueryOptions()); + } + + @Override + public CompletableFuture> query(String query, Class type) { + return query(query, type, null, new AqlQueryOptions()); + } + + @Override + public CompletableFuture> cursor(final String cursorId, final Class type) { + return cursor(cursorId, type, null); + } + + @Override + public CompletableFuture> cursor(final String cursorId, final Class type, final String nextBatchId) { + final HostHandle hostHandle = new HostHandle(); + return executorAsync() + .execute( + queryNextRequest(cursorId, new AqlQueryOptions(), nextBatchId), + cursorEntityDeserializer(type), + hostHandle) + .thenApply(res -> new ArangoCursorAsyncImpl<>(this, res, type, hostHandle, nextBatchId != null)); + } @Override public CompletableFuture explainQuery( diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java index 7c5cd151f..dd5b1ef27 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java @@ -189,19 +189,14 @@ public ArangoCursor query(final String query, final Class type) { @Override public ArangoCursor cursor(final String cursorId, final Class type) { - final HostHandle hostHandle = new HostHandle(); - final InternalCursorEntity result = executorSync().execute( - queryNextRequest(cursorId, null), - internalCursorEntityDeserializer(), - hostHandle); - return createCursor(result, type, null, hostHandle); + return cursor(cursorId, type, null); } @Override public ArangoCursor cursor(final String cursorId, final Class type, final String nextBatchId) { final HostHandle hostHandle = new HostHandle(); final InternalCursorEntity result = executorSync().execute( - queryNextByBatchIdRequest(cursorId, nextBatchId, null), + queryNextRequest(cursorId, new AqlQueryOptions(), nextBatchId), internalCursorEntityDeserializer(), hostHandle); return createCursor(result, type, null, hostHandle); @@ -216,9 +211,7 @@ private ArangoCursor createCursor( final ArangoCursorExecute execute = new ArangoCursorExecute() { @Override public InternalCursorEntity next(final String id, final String nextBatchId) { - InternalRequest request = nextBatchId == null ? - queryNextRequest(id, options) : queryNextByBatchIdRequest(id, nextBatchId, options); - return executorSync().execute(request, internalCursorEntityDeserializer(), hostHandle); + return executorSync().execute(queryNextRequest(id, options, nextBatchId), internalCursorEntityDeserializer(), hostHandle); } @Override diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java b/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java index f8b9fabe6..31e68e043 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java @@ -59,6 +59,7 @@ private ArangoExecuteable(final ArangoExecutorSync executorSync, protected static String createPath(final String... params) { final StringBuilder sb = new StringBuilder(); for (int i = 0; i < params.length; i++) { + if (params[i] == null) continue; if (i > 0) { sb.append(SLASH); } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoCursor.java b/core/src/main/java/com/arangodb/internal/InternalArangoCursor.java new file mode 100644 index 000000000..d0aea6634 --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/InternalArangoCursor.java @@ -0,0 +1,111 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.internal; + +import com.arangodb.BaseArangoCursor; +import com.arangodb.entity.CursorEntity; + +import java.util.List; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +public abstract class InternalArangoCursor extends ArangoExecuteable implements BaseArangoCursor { + + private static final String PATH_API_CURSOR = "/_api/cursor"; + + private final String dbName; + private final CursorEntity entity; + private final Class type; + private final boolean allowRetry; + + protected InternalArangoCursor( + final ArangoExecuteable executeable, + final String dbName, + final CursorEntity entity, + final Class type, + final Boolean allowRetry + ) { + super(executeable); + this.dbName = dbName; + this.entity = entity; + this.type = type; + this.allowRetry = Boolean.TRUE.equals(allowRetry); + } + + @Override + public String getId() { + return entity.getId(); + } + + @Override + public Long getCount() { + return entity.getCount(); + } + + @Override + public Boolean isCached() { + return entity.getCached(); + } + + @Override + public Boolean hasMore() { + return entity.getHasMore(); + } + + @Override + public List getResult() { + return entity.getResult(); + } + + @Override + public Boolean isPotentialDirtyRead() { + return entity.isPotentialDirtyRead(); + } + + @Override + public String getNextBatchId() { + return entity.getNextBatchId(); + } + + @Override + public CursorEntity.Extra getExtra() { + return entity.getExtra(); + } + + protected boolean allowRetry() { + return allowRetry; + } + + protected Class getType() { + return type; + } + + protected InternalRequest queryNextRequest() { + return request(dbName, RequestType.POST, PATH_API_CURSOR, entity.getId(), entity.getNextBatchId()); + } + + protected InternalRequest queryCloseRequest() { + return request(dbName, RequestType.DELETE, PATH_API_CURSOR, entity.getId()); + } + +} diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java index 9d54a5ae3..6d698f176 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java @@ -32,6 +32,7 @@ import java.util.Map; import static com.arangodb.internal.serde.SerdeUtils.constructListType; +import static com.arangodb.internal.serde.SerdeUtils.constructParametricType; /** * @author Mark Vollmary @@ -152,19 +153,8 @@ protected InternalRequest queryRequest(final String query, final Map internalCursorEntityDeseria }; } + public ResponseDeserializer> cursorEntityDeserializer(final Class type) { + return response -> { + CursorEntity e = getSerde().deserialize(response.getBody(), constructParametricType(CursorEntity.class, type)); + boolean potentialDirtyRead = Boolean.parseBoolean(response.getMeta("X-Arango-Potential-Dirty-Read")); + e.setPotentialDirtyRead(potentialDirtyRead); + return e; + }; + } + protected ResponseDeserializer deleteAqlFunctionResponseDeserializer() { return response -> getSerde().deserialize(response.getBody(), "/deletedCount", Integer.class); } diff --git a/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorAsyncImpl.java b/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorAsyncImpl.java new file mode 100644 index 000000000..d637df84c --- /dev/null +++ b/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorAsyncImpl.java @@ -0,0 +1,50 @@ +package com.arangodb.internal.cursor; + +import com.arangodb.ArangoCursorAsync; +import com.arangodb.entity.CursorEntity; +import com.arangodb.internal.ArangoDatabaseAsyncImpl; +import com.arangodb.internal.InternalArangoCursor; +import com.arangodb.internal.net.HostHandle; + +import java.util.NoSuchElementException; +import java.util.concurrent.CompletableFuture; + +public class ArangoCursorAsyncImpl extends InternalArangoCursor implements ArangoCursorAsync { + + private final ArangoDatabaseAsyncImpl db; + private final HostHandle hostHandle; + + public ArangoCursorAsyncImpl( + final ArangoDatabaseAsyncImpl db, + final CursorEntity entity, + final Class type, + final HostHandle hostHandle, + final Boolean allowRetry + ) { + super(db, db.name(), entity, type, allowRetry); + this.db = db; + this.hostHandle = hostHandle; + } + + @Override + public CompletableFuture> nextBatch() { + if (Boolean.TRUE.equals(hasMore())) { + return executorAsync().execute(queryNextRequest(), db.cursorEntityDeserializer(getType()), hostHandle) + .thenApply(r -> new ArangoCursorAsyncImpl<>(db, r, getType(), hostHandle, allowRetry())); + } else { + return CompletableFuture.supplyAsync(() -> { + throw new NoSuchElementException(); + }); + } + } + + @Override + public CompletableFuture close() { + if (getId() != null && (allowRetry() || Boolean.TRUE.equals(hasMore()))) { + return executorAsync().execute(queryCloseRequest(), Void.class, hostHandle); + } else { + return CompletableFuture.completedFuture(null); + } + } + +} diff --git a/driver/src/test/java/CommunicationTest.java b/driver/src/test/java/CommunicationTest.java new file mode 100644 index 000000000..7ab522187 --- /dev/null +++ b/driver/src/test/java/CommunicationTest.java @@ -0,0 +1,74 @@ +import com.arangodb.*; +import com.arangodb.config.ArangoConfigProperties; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class CommunicationTest { + + @ParameterizedTest + @EnumSource(Protocol.class) + @Timeout(5) + void disconnectAsync(Protocol protocol) throws InterruptedException, ExecutionException { + // FIXME: fix for VST protocol (DE-708) + assumeTrue(!Protocol.VST.equals(protocol)); + + ArangoDBAsync arangoDB = new ArangoDB.Builder() + .loadProperties(ArangoConfigProperties.fromFile()) + .protocol(protocol) + .build() + .async(); + arangoDB.getVersion().get(); + + CompletableFuture> result = arangoDB.db().query("return sleep(1)", null, null, null); + Thread.sleep(500); + arangoDB.shutdown(); + Throwable thrown = catchThrowable(result::get).getCause(); + assertThat(thrown) + .isNotNull() + .isInstanceOf(ArangoDBException.class); + assertThat(thrown.getCause()) + .isInstanceOf(IOException.class) + .hasMessageContaining("closed"); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + @Timeout(5) + void disconnect(Protocol protocol) { + // FIXME: fix for VST protocol (DE-708) + assumeTrue(!Protocol.VST.equals(protocol)); + + ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(ArangoConfigProperties.fromFile()) + .protocol(protocol) + .build(); + arangoDB.getVersion(); + + new Thread(() -> { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + arangoDB.shutdown(); + }).start(); + + Throwable thrown = catchThrowable(() -> arangoDB.db().query("return sleep(1)", null, null, null)); + assertThat(thrown) + .isNotNull() + .isInstanceOf(ArangoDBException.class); + assertThat(thrown.getCause()) + .isInstanceOf(IOException.class) + .hasMessageContaining("closed"); + } + +} diff --git a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java index 6de65b0bb..039a5f254 100644 --- a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java @@ -31,13 +31,16 @@ import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; +import java.util.stream.IntStream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; @@ -191,7 +194,8 @@ void createDatabaseWithUsers(ArangoDBAsync arangoDB) throws InterruptedException .password("testPasswd") .build() .async(); -// check if testUser has been created and can access the created db + + // check if testUser has been created and can access the created db ArangoCollectionAsync collection = arangoDBTestUser.db(dbName).collection("col-" + UUID.randomUUID()); collection.create().get(); arangoDBTestUser.shutdown(); @@ -616,44 +620,41 @@ void accessMultipleDatabases(ArangoDBAsync arangoDB) throws ExecutionException, assertThat(version2).isNotNull(); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncArangos") -// @Disabled("Manual execution only") -// void queueTime(ArangoDBAsync arangoDB) throws InterruptedException, ExecutionException { -// List> futures = IntStream.range(0, 80) -// .mapToObj(i -> CompletableFuture.runAsync( -// () -> arangoDB.db().query("RETURN SLEEP(1)", Void.class), -// Executors.newFixedThreadPool(80)) -// ) -// .collect(Collectors.toList()); -// for (CompletableFuture f : futures) { -// f.get(); -// } -// -// QueueTimeMetrics qt = arangoDB.metrics().getQueueTime(); -// double avg = qt.getAvg(); -// QueueTimeSample[] values = qt.getValues(); -// if (isAtLeastVersion(3, 9)) { -// assertThat(values).hasSize(20); -// for (int i = 0; i < values.length; i++) { -// assertThat(values[i].value).isNotNegative(); -// if (i > 0) { -// assertThat(values[i].timestamp).isGreaterThanOrEqualTo(values[i - 1].timestamp); -// } -// } -// -// if (avg < 0.0) { -// System.err.println("avg < 0: " + avg); -// System.err.println("got values:"); -// for (QueueTimeSample v : values) { -// System.err.println(v.value); -// } -// } -// assertThat(avg).isNotNegative(); -// } else { -// assertThat(avg).isEqualTo(0.0); -// assertThat(values).isEmpty(); -// } -// -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + @Disabled("Manual execution only") + void queueTime(ArangoDBAsync arangoDB) throws InterruptedException, ExecutionException { + List> futures = IntStream.range(0, 80) + .mapToObj(i -> arangoDB.db().query("RETURN SLEEP(1)", Void.class)) + .collect(Collectors.toList()); + for (CompletableFuture f : futures) { + f.get(); + } + + QueueTimeMetrics qt = arangoDB.metrics().getQueueTime(); + double avg = qt.getAvg(); + QueueTimeSample[] values = qt.getValues(); + if (isAtLeastVersion(3, 9)) { + assertThat(values).hasSize(20); + for (int i = 0; i < values.length; i++) { + assertThat(values[i].value).isNotNegative(); + if (i > 0) { + assertThat(values[i].timestamp).isGreaterThanOrEqualTo(values[i - 1].timestamp); + } + } + + if (avg < 0.0) { + System.err.println("avg < 0: " + avg); + System.err.println("got values:"); + for (QueueTimeSample v : values) { + System.err.println(v.value); + } + } + assertThat(avg).isNotNegative(); + } else { + assertThat(avg).isEqualTo(0.0); + assertThat(values).isEmpty(); + } + + } } diff --git a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java index 8c4a32572..9186b21a9 100644 --- a/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDatabaseAsyncTest.java @@ -25,6 +25,7 @@ import com.arangodb.entity.QueryCachePropertiesEntity.CacheMode; import com.arangodb.model.*; import com.arangodb.util.MapBuilder; +import com.arangodb.util.RawBytes; import com.arangodb.util.RawJson; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -35,6 +36,7 @@ import org.junit.jupiter.params.provider.MethodSource; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import static org.assertj.core.api.Assertions.assertThat; @@ -339,7 +341,7 @@ void createCollectionWithJsonSchema(ArangoDatabaseAsync db) throws ExecutionExce BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); doc.addAttribute("number", 33); - db.collection(name).insertDocument(doc); + db.collection(name).insertDocument(doc).get(); BaseDocument wrongDoc = new BaseDocument(UUID.randomUUID().toString()); wrongDoc.addAttribute("number", "notANumber"); @@ -554,167 +556,108 @@ void getPermissions(ArangoDatabaseAsync db) throws ExecutionException, Interrupt assertThat(db.getPermissions("root").get()).isEqualTo(Permissions.RW); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void query(ArangoDatabaseAsync db) { -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// final ArangoCursor cursor = db.query("for i in " + CNAME1 + " return i._id", String.class); -// assertThat((Object) cursor).isNotNull(); -// for (int i = 0; i < 10; i++, cursor.next()) { -// assertThat((Iterator) cursor).hasNext(); -// } -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithNullBindVar(ArangoDatabaseAsync db) { -// final ArangoCursor cursor = db.query("return @foo", Object.class, Collections.singletonMap("foo", null)); -// assertThat(cursor.hasNext()).isTrue(); -// assertThat(cursor.next()).isNull(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryForEach(ArangoDatabaseAsync db) { -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// final ArangoCursor cursor = db.query("for i in " + CNAME1 + " return i._id", String.class); -// assertThat((Object) cursor).isNotNull(); -// -// int i = 0; -// while (cursor.hasNext()) { -// cursor.next(); -// i++; -// } -// assertThat(i).isGreaterThanOrEqualTo(10); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithCount(ArangoDatabaseAsync db) { -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// -// final ArangoCursor cursor = db -// .query("for i in " + CNAME1 + " Limit 6 return i._id", String.class, new AqlQueryOptions().count(true)); -// assertThat((Object) cursor).isNotNull(); -// for (int i = 1; i <= 6; i++, cursor.next()) { -// assertThat(cursor.hasNext()).isTrue(); -// } -// assertThat(cursor.getCount()).isEqualTo(6); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithLimitAndFullCount(ArangoDatabaseAsync db) { -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// -// final ArangoCursor cursor = db -// .query("for i in " + CNAME1 + " Limit 5 return i._id", String.class, new AqlQueryOptions().fullCount(true)); -// assertThat((Object) cursor).isNotNull(); -// for (int i = 0; i < 5; i++, cursor.next()) { -// assertThat((Iterator) cursor).hasNext(); -// } -// assertThat(cursor.getStats()).isNotNull(); -// assertThat(cursor.getStats().getExecutionTime()).isPositive(); -// assertThat((cursor.getStats().getFullCount())).isGreaterThanOrEqualTo(10); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryStats(ArangoDatabaseAsync db) { -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// -// final ArangoCursor cursor = db.query("for i in " + CNAME1 + " return i", Object.class); -// assertThat((Object) cursor).isNotNull(); -// for (int i = 0; i < 5; i++, cursor.next()) { -// assertThat((Iterator) cursor).hasNext(); -// } -// assertThat(cursor.getStats()).isNotNull(); -// assertThat(cursor.getStats().getWritesExecuted()).isNotNull(); -// assertThat(cursor.getStats().getWritesIgnored()).isNotNull(); -// assertThat(cursor.getStats().getScannedFull()).isNotNull(); -// assertThat(cursor.getStats().getScannedIndex()).isNotNull(); -// assertThat(cursor.getStats().getFiltered()).isNotNull(); -// assertThat(cursor.getStats().getExecutionTime()).isNotNull(); -// assertThat(cursor.getStats().getPeakMemoryUsage()).isNotNull(); -// if (isAtLeastVersion(3, 10)) { -// assertThat(cursor.getStats().getCursorsCreated()).isNotNull(); -// assertThat(cursor.getStats().getCursorsRearmed()).isNotNull(); -// assertThat(cursor.getStats().getCacheHits()).isNotNull(); -// assertThat(cursor.getStats().getCacheMisses()).isNotNull(); -// } -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithBatchSize(ArangoDatabaseAsync db) { -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// -// final ArangoCursor cursor = db -// .query("for i in " + CNAME1 + " return i._id", String.class, new AqlQueryOptions().batchSize(5).count(true)); -// -// assertThat((Object) cursor).isNotNull(); -// for (int i = 0; i < 10; i++, cursor.next()) { -// assertThat((Iterator) cursor).hasNext(); -// } -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryIterateWithBatchSize(ArangoDatabaseAsync db) { -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// -// final ArangoCursor cursor = db -// .query("for i in " + CNAME1 + " return i._id", String.class, new AqlQueryOptions().batchSize(5).count(true)); -// -// assertThat((Object) cursor).isNotNull(); -// final AtomicInteger i = new AtomicInteger(0); -// for (; cursor.hasNext(); cursor.next()) { -// i.incrementAndGet(); -// } -// assertThat(i.get()).isGreaterThanOrEqualTo(10); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithTTL(ArangoDatabaseAsync db) throws InterruptedException { -// // set TTL to 1 seconds and get the second batch after 2 seconds! -// final int ttl = 1; -// final int wait = 2; -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// -// final ArangoCursor cursor = db -// .query("for i in " + CNAME1 + " return i._id", String.class, new AqlQueryOptions().batchSize(5).ttl(ttl)); -// -// assertThat((Iterable) cursor).isNotNull(); -// -// try { -// for (int i = 0; i < 10; i++, cursor.next()) { -// assertThat(cursor.hasNext()).isTrue(); -// if (i == 1) { -// Thread.sleep(wait * 1000); -// } -// } -// fail("this should fail"); -// } catch (final ArangoDBException ex) { -// assertThat(ex.getMessage()).isEqualTo("Response: 404, Error: 1600 - cursor not found"); -// } -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void query(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + ArangoCursorAsync cursor = db.query("for i in 0..9 return i", Integer.class).get(); + List res = cursor.getResult(); + assertThat(res).hasSize(10); + for (int i = 0; i < 10; i++) { + assertThat(res.get(i)).isEqualTo(i); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithNullBindVar(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final ArangoCursorAsync cursor = db.query("return @foo", Object.class, Collections.singletonMap("foo", null)).get(); + assertThat(cursor.getResult()).containsExactly((Object) null); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryForEach(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + for (int i = 0; i < 10; i++) { + db.collection(CNAME1).insertDocument(new BaseDocument(), null).get(); + } + final ArangoCursorAsync cursor = db.query("for i in " + CNAME1 + " return i._id", String.class).get(); + assertThat(cursor.getResult()).hasSizeGreaterThanOrEqualTo(10); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithCount(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + for (int i = 0; i < 10; i++) { + db.collection(CNAME1).insertDocument(new BaseDocument(), null).get(); + } + + final ArangoCursorAsync cursor = db + .query("for i in " + CNAME1 + " Limit 6 return i._id", String.class, new AqlQueryOptions().count(true)).get(); + assertThat(cursor.getCount()).isEqualTo(6); + assertThat(cursor.getResult()).hasSize(6); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithLimitAndFullCount(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + for (int i = 0; i < 10; i++) { + db.collection(CNAME1).insertDocument(new BaseDocument(), null).get(); + } + + final ArangoCursorAsync cursor = db + .query("for i in " + CNAME1 + " Limit 5 return i._id", String.class, new AqlQueryOptions().fullCount(true)).get(); + assertThat(cursor.getResult()).hasSize(5); + assertThat(cursor.getExtra().getStats()).isNotNull(); + assertThat(cursor.getExtra().getStats().getExecutionTime()).isPositive(); + assertThat((cursor.getExtra().getStats().getFullCount())).isGreaterThanOrEqualTo(10); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryStats(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + for (int i = 0; i < 10; i++) { + db.collection(CNAME1).insertDocument(new BaseDocument(), null).get(); + } + + final ArangoCursorAsync cursor = db.query("for i in " + CNAME1 + " return i", Object.class).get(); + assertThat(cursor.getResult()).hasSizeGreaterThanOrEqualTo(10); + assertThat(cursor.getExtra().getStats()).isNotNull(); + assertThat(cursor.getExtra().getStats().getWritesExecuted()).isNotNull(); + assertThat(cursor.getExtra().getStats().getWritesIgnored()).isNotNull(); + assertThat(cursor.getExtra().getStats().getScannedFull()).isNotNull(); + assertThat(cursor.getExtra().getStats().getScannedIndex()).isNotNull(); + assertThat(cursor.getExtra().getStats().getFiltered()).isNotNull(); + assertThat(cursor.getExtra().getStats().getExecutionTime()).isNotNull(); + assertThat(cursor.getExtra().getStats().getPeakMemoryUsage()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithBatchSize(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final ArangoCursorAsync cursor = db + .query("for i in 1..10 return i", Integer.class, new AqlQueryOptions().batchSize(5)).get(); + + assertThat(cursor.getResult()).hasSize(5); + assertThat(cursor.hasMore()).isTrue(); + + ArangoCursorAsync c2 = cursor.nextBatch().get(); + assertThat(c2.getResult()).hasSize(5); + assertThat(c2.hasMore()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithTTL(ArangoDatabaseAsync db) throws InterruptedException, ExecutionException { + final ArangoCursorAsync cursor = db + .query("for i in 1..10 return i", Integer.class, new AqlQueryOptions().batchSize(5).ttl(1)).get(); + assertThat(cursor.getResult()).hasSize(5); + assertThat(cursor.hasMore()).isTrue(); + Thread.sleep(1_000); + Throwable thrown = catchThrowable(() -> cursor.nextBatch().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException ex = (ArangoDBException) thrown; + assertThat(ex.getMessage()).isEqualTo("Response: 404, Error: 1600 - cursor not found"); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -737,115 +680,119 @@ void changeQueryCache(ArangoDatabaseAsync db) throws ExecutionException, Interru db.setQueryCacheProperties(properties2).get(); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithCache(ArangoDatabaseAsync db) { -// assumeTrue(isSingleServer()); -// for (int i = 0; i < 10; i++) { -// db.collection(CNAME1).insertDocument(new BaseDocument(), null); -// } -// -// final QueryCachePropertiesEntity properties = new QueryCachePropertiesEntity(); -// properties.setMode(CacheMode.on); -// db.setQueryCacheProperties(properties); -// -// final ArangoCursor cursor = db -// .query("FOR t IN " + CNAME1 + " FILTER t.age >= 10 SORT t.age RETURN t._id", String.class, -// new AqlQueryOptions().cache(true)); -// -// assertThat((Object) cursor).isNotNull(); -// assertThat(cursor.isCached()).isFalse(); -// -// final ArangoCursor cachedCursor = db -// .query("FOR t IN " + CNAME1 + " FILTER t.age >= 10 SORT t.age RETURN t._id", String.class, -// new AqlQueryOptions().cache(true)); -// -// assertThat((Object) cachedCursor).isNotNull(); -// assertThat(cachedCursor.isCached()).isTrue(); -// -// final QueryCachePropertiesEntity properties2 = new QueryCachePropertiesEntity(); -// properties2.setMode(CacheMode.off); -// db.setQueryCacheProperties(properties2); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithMemoryLimit(ArangoDatabaseAsync db) { -// Throwable thrown = catchThrowable(() -> db.query("RETURN 1..100000", String.class, -// new AqlQueryOptions().memoryLimit(32 * 1024L))); -// assertThat(thrown).isInstanceOf(ArangoDBException.class); -// assertThat(((ArangoDBException) thrown).getErrorNum()).isEqualTo(32); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithFailOnWarningTrue(ArangoDatabaseAsync db) { -// Throwable thrown = catchThrowable(() -> db.query("RETURN 1 / 0", String.class, -// new AqlQueryOptions().failOnWarning(true))); -// assertThat(thrown).isInstanceOf(ArangoDBException.class); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithFailOnWarningFalse(ArangoDatabaseAsync db) { -// final ArangoCursor cursor = db -// .query("RETURN 1 / 0", String.class, new AqlQueryOptions().failOnWarning(false)); -// assertThat(cursor.next()).isNull(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithTimeout(ArangoDatabaseAsync db) { -// assumeTrue(isAtLeastVersion(3, 6)); -// Throwable thrown = catchThrowable(() -> db.query("RETURN SLEEP(1)", String.class, -// new AqlQueryOptions().maxRuntime(0.1)).next()); -// assertThat(thrown).isInstanceOf(ArangoDBException.class); -// assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(410); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithMaxWarningCount(ArangoDatabaseAsync db) { -// final ArangoCursor cursorWithWarnings = db -// .query("RETURN 1 / 0", String.class, new AqlQueryOptions()); -// assertThat(cursorWithWarnings.getWarnings()).hasSize(1); -// final ArangoCursor cursorWithLimitedWarnings = db -// .query("RETURN 1 / 0", String.class, new AqlQueryOptions().maxWarningCount(0L)); -// final Collection warnings = cursorWithLimitedWarnings.getWarnings(); -// assertThat(warnings).isNullOrEmpty(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryCursor(ArangoDatabaseAsync db) { -// ArangoCursor cursor = db.query("for i in 1..4 return i", Integer.class, -// new AqlQueryOptions().batchSize(1)); -// List result = new ArrayList<>(); -// result.add(cursor.next()); -// result.add(cursor.next()); -// ArangoCursor cursor2 = db.cursor(cursor.getId(), Integer.class); -// result.add(cursor2.next()); -// result.add(cursor2.next()); -// assertThat(cursor2.hasNext()).isFalse(); -// assertThat(result).containsExactly(1, 2, 3, 4); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryCursorRetry(ArangoDatabaseAsync db) throws IOException { -// assumeTrue(isAtLeastVersion(3, 11)); -// ArangoCursor cursor = db.query("for i in 1..4 return i", Integer.class, -// new AqlQueryOptions().batchSize(1).allowRetry(true)); -// List result = new ArrayList<>(); -// result.add(cursor.next()); -// result.add(cursor.next()); -// ArangoCursor cursor2 = db.cursor(cursor.getId(), Integer.class, cursor.getNextBatchId()); -// result.add(cursor2.next()); -// result.add(cursor2.next()); -// cursor2.close(); -// assertThat(cursor2.hasNext()).isFalse(); -// assertThat(result).containsExactly(1, 2, 3, 4); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithCache(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + for (int i = 0; i < 10; i++) { + db.collection(CNAME1).insertDocument(new BaseDocument(), null).get(); + } + + final QueryCachePropertiesEntity properties = new QueryCachePropertiesEntity(); + properties.setMode(CacheMode.on); + db.setQueryCacheProperties(properties).get(); + + final ArangoCursorAsync cursor = db + .query("FOR t IN " + CNAME1 + " FILTER t.age >= 10 SORT t.age RETURN t._id", String.class, + new AqlQueryOptions().cache(true)).get(); + + assertThat((Object) cursor).isNotNull(); + assertThat(cursor.isCached()).isFalse(); + + final ArangoCursorAsync cachedCursor = db + .query("FOR t IN " + CNAME1 + " FILTER t.age >= 10 SORT t.age RETURN t._id", String.class, + new AqlQueryOptions().cache(true)).get(); + + assertThat((Object) cachedCursor).isNotNull(); + assertThat(cachedCursor.isCached()).isTrue(); + + final QueryCachePropertiesEntity properties2 = new QueryCachePropertiesEntity(); + properties2.setMode(CacheMode.off); + db.setQueryCacheProperties(properties2).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithMemoryLimit(ArangoDatabaseAsync db) { + Throwable thrown = catchThrowable(() -> db.query("RETURN 1..100000", String.class, + new AqlQueryOptions().memoryLimit(32 * 1024L)).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) thrown).getErrorNum()).isEqualTo(32); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithFailOnWarningTrue(ArangoDatabaseAsync db) { + Throwable thrown = catchThrowable(() -> db.query("RETURN 1 / 0", String.class, + new AqlQueryOptions().failOnWarning(true)).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithFailOnWarningFalse(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final ArangoCursorAsync cursor = db + .query("RETURN 1 / 0", String.class, new AqlQueryOptions().failOnWarning(false)).get(); + assertThat(cursor.getResult()).containsExactly((String) null); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithTimeout(ArangoDatabaseAsync db) { + assumeTrue(isAtLeastVersion(3, 6)); + Throwable thrown = catchThrowable(() -> db.query("RETURN SLEEP(1)", String.class, + new AqlQueryOptions().maxRuntime(0.1)).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(410); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithMaxWarningCount(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final ArangoCursorAsync cursorWithWarnings = db + .query("RETURN 1 / 0", String.class, new AqlQueryOptions()).get(); + assertThat(cursorWithWarnings.getExtra().getWarnings()).hasSize(1); + final ArangoCursorAsync cursorWithLimitedWarnings = db + .query("RETURN 1 / 0", String.class, new AqlQueryOptions().maxWarningCount(0L)).get(); + final Collection warnings = cursorWithLimitedWarnings.getExtra().getWarnings(); + assertThat(warnings).isNullOrEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryCursor(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + ArangoCursorAsync c1 = db.query("for i in 1..4 return i", Integer.class, + new AqlQueryOptions().batchSize(1)).get(); + List result = new ArrayList<>(); + result.addAll(c1.getResult()); + ArangoCursorAsync c2 = c1.nextBatch().get(); + result.addAll(c2.getResult()); + ArangoCursorAsync c3 = db.cursor(c2.getId(), Integer.class).get(); + result.addAll(c3.getResult()); + ArangoCursorAsync c4 = c3.nextBatch().get(); + result.addAll(c4.getResult()); + assertThat(c4.hasMore()).isFalse(); + assertThat(result).containsExactly(1, 2, 3, 4); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryCursorRetry(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 11)); + ArangoCursorAsync c1 = db.query("for i in 1..4 return i", Integer.class, + new AqlQueryOptions().batchSize(1).allowRetry(true)).get(); + List result = new ArrayList<>(); + result.addAll(c1.getResult()); + ArangoCursorAsync c2 = c1.nextBatch().get(); + result.addAll(c2.getResult()); + ArangoCursorAsync c3 = db.cursor(c2.getId(), Integer.class, c2.getNextBatchId()).get(); + result.addAll(c3.getResult()); + ArangoCursorAsync c4 = c3.nextBatch().get(); + result.addAll(c4.getResult()); + c4.close(); + assertThat(c4.hasMore()).isFalse(); + assertThat(result).containsExactly(1, 2, 3, 4); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -871,181 +818,174 @@ void changeQueryTrackingProperties(ArangoDatabaseAsync db) throws ExecutionExcep } } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithBindVars(ArangoDatabaseAsync db) { -// for (int i = 0; i < 10; i++) { -// final BaseDocument baseDocument = new BaseDocument(UUID.randomUUID().toString()); -// baseDocument.addAttribute("age", 20 + i); -// db.collection(CNAME1).insertDocument(baseDocument, null); -// } -// final Map bindVars = new HashMap<>(); -// bindVars.put("@coll", CNAME1); -// bindVars.put("age", 25); -// -// final ArangoCursor cursor = db -// .query("FOR t IN @@coll FILTER t.age >= @age SORT t.age RETURN t._id", String.class, bindVars); -// -// assertThat((Object) cursor).isNotNull(); -// -// for (int i = 0; i < 5; i++, cursor.next()) { -// assertThat((Iterator) cursor).hasNext(); -// } -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithRawBindVars(ArangoDatabaseAsync db) { -// final Map bindVars = new HashMap<>(); -// bindVars.put("foo", RawJson.of("\"fooValue\"")); -// bindVars.put("bar", RawBytes.of(db.getSerde().serializeUserData(11))); -// -// final JsonNode res = db.query("RETURN {foo: @foo, bar: @bar}", JsonNode.class, bindVars).next(); -// -// assertThat(res.get("foo").textValue()).isEqualTo("fooValue"); -// assertThat(res.get("bar").intValue()).isEqualTo(11); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncArangos") -// void queryWithWarning(ArangoDBAsync arangoDB) { -// final ArangoCursor cursor = arangoDB.db().query("return 1/0", String.class); -// -// assertThat((Object) cursor).isNotNull(); -// assertThat(cursor.getWarnings()).isNotNull(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryStream(ArangoDatabaseAsync db) { -// final ArangoCursor cursor = db -// .query("FOR i IN 1..2 RETURN i", Void.class, new AqlQueryOptions().stream(true).count(true)); -// assertThat((Object) cursor).isNotNull(); -// assertThat(cursor.getCount()).isNull(); -// } - -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryForceOneShardAttributeValue(ArangoDatabaseAsync db) { -// assumeTrue(isAtLeastVersion(3, 10)); -// assumeTrue(isCluster()); -// assumeTrue(isEnterprise()); -// -// String cname = "forceOneShardAttr-" + UUID.randomUUID(); -// db.createCollection(cname, new CollectionCreateOptions() -// .shardKeys("foo") -// .numberOfShards(3)); -// ArangoCollection col = db.collection(cname); -// BaseDocument doc = new BaseDocument(); -// doc.addAttribute("foo", "bar"); -// col.insertDocument(doc); -// -// ArangoCursor c1 = db -// .query("FOR d IN @@c RETURN d", BaseDocument.class, Collections.singletonMap("@c", cname), -// new AqlQueryOptions().forceOneShardAttributeValue("bar")); -// assertThat(c1.hasNext()).isTrue(); -// assertThat(c1.next().getAttribute("foo")).isEqualTo("bar"); -// -// ArangoCursor c2 = db -// .query("FOR d IN @@c RETURN d", BaseDocument.class, Collections.singletonMap("@c", cname), -// new AqlQueryOptions().forceOneShardAttributeValue("ooo")); -// assertThat(c2.hasNext()).isFalse(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncArangos") -// void queryClose(ArangoDBAsync arangoDB) throws IOException { -// final ArangoCursor cursor = arangoDB.db() -// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().batchSize(1)); -// cursor.close(); -// AtomicInteger count = new AtomicInteger(); -// Throwable thrown = catchThrowable(() -> { -// while (cursor.hasNext()) { -// cursor.next(); -// count.incrementAndGet(); -// } -// }); -// -// assertThat(thrown).isInstanceOf(ArangoDBException.class); -// assertThat(count).hasValue(1); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryNoResults(ArangoDatabaseAsync db) throws IOException { -// final ArangoCursor cursor = db -// .query("FOR i IN @@col RETURN i", BaseDocument.class, new MapBuilder().put("@col", CNAME1).get()); -// cursor.close(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryWithNullBindParam(ArangoDatabaseAsync db) throws IOException { -// final ArangoCursor cursor = db.query("FOR i IN @@col FILTER i.test == @test RETURN i", -// BaseDocument.class, new MapBuilder().put("@col", CNAME1).put("test", null).get()); -// cursor.close(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void queryAllowDirtyRead(ArangoDatabaseAsync db) throws IOException { -// final ArangoCursor cursor = db.query("FOR i IN @@col FILTER i.test == @test RETURN i", -// BaseDocument.class, new MapBuilder().put("@col", CNAME1).put("test", null).get(), -// new AqlQueryOptions().allowDirtyRead(true)); -// if (isAtLeastVersion(3, 10)) { -// assertThat(cursor.isPotentialDirtyRead()).isTrue(); -// } -// cursor.close(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncArangos") -// void queryAllowRetry(ArangoDBAsync arangoDB) throws IOException { -// assumeTrue(isAtLeastVersion(3, 11)); -// final ArangoCursor cursor = arangoDB.db() -// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)); -// assertThat(cursor.asListRemaining()).containsExactly("1", "2"); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncArangos") -// void queryAllowRetryClose(ArangoDBAsync arangoDB) throws IOException { -// assumeTrue(isAtLeastVersion(3, 11)); -// final ArangoCursor cursor = arangoDB.db() -// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)); -// assertThat(cursor.hasNext()).isTrue(); -// assertThat(cursor.next()).isEqualTo("1"); -// assertThat(cursor.hasNext()).isTrue(); -// assertThat(cursor.next()).isEqualTo("2"); -// assertThat(cursor.hasNext()).isFalse(); -// cursor.close(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncArangos") -// void queryAllowRetryCloseBeforeLatestBatch(ArangoDBAsync arangoDB) throws IOException { -// assumeTrue(isAtLeastVersion(3, 11)); -// final ArangoCursor cursor = arangoDB.db() -// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)); -// assertThat(cursor.hasNext()).isTrue(); -// assertThat(cursor.next()).isEqualTo("1"); -// assertThat(cursor.hasNext()).isTrue(); -// cursor.close(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncArangos") -// void queryAllowRetryCloseSingleBatch(ArangoDBAsync arangoDB) throws IOException { -// assumeTrue(isAtLeastVersion(3, 11)); -// final ArangoCursor cursor = arangoDB.db() -// .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true)); -// assertThat(cursor.hasNext()).isTrue(); -// assertThat(cursor.next()).isEqualTo("1"); -// assertThat(cursor.hasNext()).isTrue(); -// assertThat(cursor.next()).isEqualTo("2"); -// assertThat(cursor.hasNext()).isFalse(); -// cursor.close(); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithBindVars(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + for (int i = 0; i < 10; i++) { + final BaseDocument baseDocument = new BaseDocument(UUID.randomUUID().toString()); + baseDocument.addAttribute("age", 20 + i); + db.collection(CNAME1).insertDocument(baseDocument, null).get(); + } + final Map bindVars = new HashMap<>(); + bindVars.put("@coll", CNAME1); + bindVars.put("age", 25); + + final ArangoCursorAsync cursor = db + .query("FOR t IN @@coll FILTER t.age >= @age SORT t.age RETURN t._id", String.class, bindVars).get(); + + assertThat(cursor.getResult()).hasSizeGreaterThanOrEqualTo(5); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithRawBindVars(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final Map bindVars = new HashMap<>(); + bindVars.put("foo", RawJson.of("\"fooValue\"")); + bindVars.put("bar", RawBytes.of(db.getSerde().serializeUserData(11))); + + final JsonNode res = db.query("RETURN {foo: @foo, bar: @bar}", JsonNode.class, bindVars).get() + .getResult().get(0); + + assertThat(res.get("foo").textValue()).isEqualTo("fooValue"); + assertThat(res.get("bar").intValue()).isEqualTo(11); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void queryWithWarning(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + final ArangoCursorAsync cursor = arangoDB.db().query("return 1/0", String.class).get(); + assertThat(cursor.getExtra().getWarnings()) + .isNotNull() + .hasSize(1) + .allSatisfy(w -> assertThat(w.getMessage()).contains("division by zero")); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryStream(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final ArangoCursorAsync cursor = db + .query("FOR i IN 1..2 RETURN i", Void.class, new AqlQueryOptions().stream(true).count(true)).get(); + assertThat((Object) cursor).isNotNull(); + assertThat(cursor.getCount()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryForceOneShardAttributeValue(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + assumeTrue(isCluster()); + assumeTrue(isEnterprise()); + + String cname = "forceOneShardAttr-" + UUID.randomUUID(); + db.createCollection(cname, new CollectionCreateOptions() + .shardKeys("foo") + .numberOfShards(3)).get(); + ArangoCollectionAsync col = db.collection(cname); + BaseDocument doc = new BaseDocument(); + doc.addAttribute("foo", "bar"); + col.insertDocument(doc).get(); + + Iterator c1 = db + .query("FOR d IN @@c RETURN d", BaseDocument.class, Collections.singletonMap("@c", cname), + new AqlQueryOptions().forceOneShardAttributeValue("bar")).get().getResult().iterator(); + assertThat(c1.hasNext()).isTrue(); + assertThat(c1.next().getAttribute("foo")).isEqualTo("bar"); + + Iterator c2 = db + .query("FOR d IN @@c RETURN d", BaseDocument.class, Collections.singletonMap("@c", cname), + new AqlQueryOptions().forceOneShardAttributeValue("ooo")).get().getResult().iterator(); + assertThat(c2.hasNext()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void queryClose(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + final ArangoCursorAsync cursor = arangoDB.db() + .query("for i in 1..2 return i", String.class, new AqlQueryOptions().batchSize(1)).get(); + cursor.close().get(); + assertThat(cursor.getResult()).hasSize(1); + Throwable thrown = catchThrowable(() -> cursor.nextBatch().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException ex = (ArangoDBException) thrown; + assertThat(ex.getResponseCode()).isEqualTo(404); + assertThat(ex.getMessage()).contains("cursor not found"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryNoResults(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + db.query("FOR i IN @@col RETURN i", BaseDocument.class, new MapBuilder().put("@col", CNAME1).get()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryWithNullBindParam(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + db.query("FOR i IN @@col FILTER i.test == @test RETURN i", BaseDocument.class, + new MapBuilder().put("@col", CNAME1).put("test", null).get()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void queryAllowDirtyRead(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + final ArangoCursorAsync cursor = db.query("FOR i IN @@col FILTER i.test == @test RETURN i", + BaseDocument.class, new MapBuilder().put("@col", CNAME1).put("test", null).get(), + new AqlQueryOptions().allowDirtyRead(true)).get(); + assertThat(cursor.isPotentialDirtyRead()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void queryAllowRetry(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 11)); + final ArangoCursorAsync cursor = arangoDB.db() + .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)).get(); + assertThat(cursor.getResult()).containsExactly("1"); + assertThat(cursor.hasMore()).isTrue(); + cursor.nextBatch().get(); + cursor.nextBatch().get(); + + ArangoCursorAsync c2 = cursor.nextBatch().get(); + assertThat(c2.getResult()).containsExactly("2"); + assertThat(c2.hasMore()).isFalse(); + + cursor.close().get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void queryAllowRetryClose(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 11)); + final ArangoCursorAsync cursor = arangoDB.db() + .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)).get(); + assertThat(cursor.getResult()).containsExactly("1"); + assertThat(cursor.hasMore()).isTrue(); + ArangoCursorAsync c2 = cursor.nextBatch().get(); + assertThat(c2.getResult()).containsExactly("2"); + assertThat(c2.hasMore()).isFalse(); + c2.close().get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void queryAllowRetryCloseBeforeLatestBatch(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 11)); + final ArangoCursorAsync cursor = arangoDB.db() + .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true).batchSize(1)).get(); + assertThat(cursor.getResult()).containsExactly("1"); + assertThat(cursor.hasMore()).isTrue(); + cursor.close().get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangos") + void queryAllowRetryCloseSingleBatch(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 11)); + final ArangoCursorAsync cursor = arangoDB.db() + .query("for i in 1..2 return i", String.class, new AqlQueryOptions().allowRetry(true)).get(); + assertThat(cursor.getResult()).containsExactly("1", "2"); + assertThat(cursor.hasMore()).isFalse(); + cursor.close().get(); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -1120,94 +1060,87 @@ void parseQuery(ArangoDatabaseAsync db) throws ExecutionException, InterruptedEx assertThat(parse.getAst()).hasSize(1); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void getCurrentlyRunningQueries(ArangoDatabaseAsync db) throws InterruptedException { -// String query = "return sleep(1)"; -// Thread t = new Thread(() -> db.query(query, Void.class)); -// t.start(); -// Thread.sleep(300); -// final Collection currentlyRunningQueries = db.getCurrentlyRunningQueries(); -// assertThat(currentlyRunningQueries).hasSize(1); -// final QueryEntity queryEntity = currentlyRunningQueries.iterator().next(); -// assertThat(queryEntity.getId()).isNotNull(); -// assertThat(queryEntity.getDatabase()).isEqualTo(db.name()); -// assertThat(queryEntity.getUser()).isEqualTo("root"); -// assertThat(queryEntity.getQuery()).isEqualTo(query); -// assertThat(queryEntity.getBindVars()).isEmpty(); -// assertThat(queryEntity.getStarted()).isInThePast(); -// assertThat(queryEntity.getRunTime()).isPositive(); -// if (isAtLeastVersion(3, 11)) { -// assertThat(queryEntity.getPeakMemoryUsage()).isNotNull(); -// } -// assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.EXECUTING); -// assertThat(queryEntity.getStream()).isFalse(); -// t.join(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void killQuery(ArangoDatabaseAsync db) throws InterruptedException, ExecutionException { -// ExecutorService es = Executors.newSingleThreadExecutor(); -// Future future = es.submit(() -> { -// try { -// db.query("return sleep(5)", Void.class); -// fail(); -// } catch (ArangoDBException e) { -// assertThat(e.getResponseCode()).isEqualTo(410); -// assertThat(e.getErrorNum()).isEqualTo(1500); -// assertThat(e.getErrorMessage()).contains("query killed"); -// } -// }); -// Thread.sleep(500); -// -// Collection currentlyRunningQueries = db.getCurrentlyRunningQueries(); -// assertThat(currentlyRunningQueries).hasSize(1); -// QueryEntity queryEntity = currentlyRunningQueries.iterator().next(); -// assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.EXECUTING); -// db.killQuery(queryEntity.getId()); -// -// db.getCurrentlyRunningQueries().forEach(q -> -// assertThat(q.getState()).isEqualTo(QueryExecutionState.KILLED) -// ); -// -// future.get(); -// es.shutdown(); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void getAndClearSlowQueries(ArangoDatabaseAsync db) { -// db.clearSlowQueries(); -// -// final QueryTrackingPropertiesEntity properties = db.getQueryTrackingProperties(); -// final Long slowQueryThreshold = properties.getSlowQueryThreshold(); -// properties.setSlowQueryThreshold(1L); -// db.setQueryTrackingProperties(properties); -// -// String query = "return sleep(1.1)"; -// db.query(query, Void.class); -// final Collection slowQueries = db.getSlowQueries(); -// assertThat(slowQueries).hasSize(1); -// final QueryEntity queryEntity = slowQueries.iterator().next(); -// assertThat(queryEntity.getId()).isNotNull(); -// assertThat(queryEntity.getDatabase()).isEqualTo(db.name()); -// assertThat(queryEntity.getUser()).isEqualTo("root"); -// assertThat(queryEntity.getQuery()).isEqualTo(query); -// assertThat(queryEntity.getBindVars()).isEmpty(); -// assertThat(queryEntity.getStarted()).isInThePast(); -// assertThat(queryEntity.getRunTime()).isPositive(); -// if (isAtLeastVersion(3, 11)) { -// assertThat(queryEntity.getPeakMemoryUsage()).isNotNull(); -// } -// assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.FINISHED); -// assertThat(queryEntity.getStream()).isFalse(); -// -// db.clearSlowQueries(); -// assertThat(db.getSlowQueries()).isEmpty(); -// properties.setSlowQueryThreshold(slowQueryThreshold); -// db.setQueryTrackingProperties(properties); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getCurrentlyRunningQueries(ArangoDatabaseAsync db) throws InterruptedException, ExecutionException { + String query = "return sleep(1)"; + CompletableFuture> q = db.query(query, Void.class); + Thread.sleep(300); + final Collection currentlyRunningQueries = db.getCurrentlyRunningQueries().get(); + assertThat(currentlyRunningQueries).hasSize(1); + final QueryEntity queryEntity = currentlyRunningQueries.iterator().next(); + assertThat(queryEntity.getId()).isNotNull(); + assertThat(queryEntity.getDatabase()).isEqualTo(db.name()); + assertThat(queryEntity.getUser()).isEqualTo("root"); + assertThat(queryEntity.getQuery()).isEqualTo(query); + assertThat(queryEntity.getBindVars()).isEmpty(); + assertThat(queryEntity.getStarted()).isInThePast(); + assertThat(queryEntity.getRunTime()).isPositive(); + if (isAtLeastVersion(3, 11)) { + assertThat(queryEntity.getPeakMemoryUsage()).isNotNull(); + } + assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.EXECUTING); + assertThat(queryEntity.getStream()).isFalse(); + q.get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void killQuery(ArangoDatabaseAsync db) throws InterruptedException, ExecutionException { + CompletableFuture> c = db.query("return sleep(5)", Void.class); + Thread.sleep(500); + + Collection currentlyRunningQueries = db.getCurrentlyRunningQueries().get(); + assertThat(currentlyRunningQueries).hasSize(1); + QueryEntity queryEntity = currentlyRunningQueries.iterator().next(); + assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.EXECUTING); + db.killQuery(queryEntity.getId()).get(); + + db.getCurrentlyRunningQueries().get().forEach(q -> + assertThat(q.getState()).isEqualTo(QueryExecutionState.KILLED) + ); + + Throwable thrown = catchThrowable(c::get).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(410); + assertThat(e.getErrorNum()).isEqualTo(1500); + assertThat(e.getErrorMessage()).contains("query killed"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void getAndClearSlowQueries(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + db.clearSlowQueries().get(); + + final QueryTrackingPropertiesEntity properties = db.getQueryTrackingProperties().get(); + final Long slowQueryThreshold = properties.getSlowQueryThreshold(); + properties.setSlowQueryThreshold(1L); + db.setQueryTrackingProperties(properties).get(); + + String query = "return sleep(1.1)"; + db.query(query, Void.class).get(); + final Collection slowQueries = db.getSlowQueries().get(); + assertThat(slowQueries).hasSize(1); + final QueryEntity queryEntity = slowQueries.iterator().next(); + assertThat(queryEntity.getId()).isNotNull(); + assertThat(queryEntity.getDatabase()).isEqualTo(db.name()); + assertThat(queryEntity.getUser()).isEqualTo("root"); + assertThat(queryEntity.getQuery()).isEqualTo(query); + assertThat(queryEntity.getBindVars()).isEmpty(); + assertThat(queryEntity.getStarted()).isInThePast(); + assertThat(queryEntity.getRunTime()).isPositive(); + if (isAtLeastVersion(3, 11)) { + assertThat(queryEntity.getPeakMemoryUsage()).isNotNull(); + } + assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.FINISHED); + assertThat(queryEntity.getStream()).isFalse(); + + db.clearSlowQueries().get(); + assertThat(db.getSlowQueries().get()).isEmpty(); + properties.setSlowQueryThreshold(slowQueryThreshold); + db.setQueryTrackingProperties(properties).get(); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") diff --git a/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java index 7713ae878..d8a586ce3 100644 --- a/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoSearchAsyncTest.java @@ -768,37 +768,37 @@ void pipelineAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, Interru createGetAndDeleteTypedAnalyzer(db, pipelineAnalyzer); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void stopwordsAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { -// assumeTrue(isAtLeastVersion(3, 8)); -// -// Set features = new HashSet<>(); -// features.add(AnalyzerFeature.frequency); -// features.add(AnalyzerFeature.norm); -// features.add(AnalyzerFeature.position); -// -// StopwordsAnalyzerProperties properties = new StopwordsAnalyzerProperties() -// .addStopwordAsHex("616e64") -// .addStopwordAsString("the"); -// -// assertThat(properties.getStopwordsAsStringList()).contains("and"); -// assertThat(properties.getStopwordsAsHexList()).contains("746865"); -// -// StopwordsAnalyzer analyzer = new StopwordsAnalyzer(); -// String name = "test-" + UUID.randomUUID(); -// analyzer.setName(name); -// analyzer.setProperties(properties); -// analyzer.setFeatures(features); -// -// createGetAndDeleteTypedAnalyzer(db, analyzer); -// db.createSearchAnalyzer(analyzer); -// Collection res = db.query("RETURN FLATTEN(TOKENS(SPLIT('the fox and the dog and a theater', ' '), " + -// "@aName))", Collection.class, -// Collections.singletonMap("aName", name)).next(); -// assertThat(res).containsExactly("fox", "dog", "a", "theater"); -// db.deleteSearchAnalyzer(name); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void stopwordsAnalyzer(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 8)); + + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + StopwordsAnalyzerProperties properties = new StopwordsAnalyzerProperties() + .addStopwordAsHex("616e64") + .addStopwordAsString("the"); + + assertThat(properties.getStopwordsAsStringList()).contains("and"); + assertThat(properties.getStopwordsAsHexList()).contains("746865"); + + StopwordsAnalyzer analyzer = new StopwordsAnalyzer(); + String name = "test-" + UUID.randomUUID(); + analyzer.setName(name); + analyzer.setProperties(properties); + analyzer.setFeatures(features); + + createGetAndDeleteTypedAnalyzer(db, analyzer); + db.createSearchAnalyzer(analyzer).get(); + Collection res = db.query("RETURN FLATTEN(TOKENS(SPLIT('the fox and the dog and a theater', ' '), " + + "@aName))", Collection.class, + Collections.singletonMap("aName", name)).get().getResult().get(0); + assertThat(res).containsExactly("fox", "dog", "a", "theater"); + db.deleteSearchAnalyzer(name).get(); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") diff --git a/driver/src/test/java/com/arangodb/ConcurrencyAsyncTests.java b/driver/src/test/java/com/arangodb/ConcurrencyAsyncTests.java new file mode 100644 index 000000000..fb10606c6 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ConcurrencyAsyncTests.java @@ -0,0 +1,92 @@ +package com.arangodb; + +import com.arangodb.config.ConfigUtils; +import com.arangodb.entity.ArangoDBVersion; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.fail; + +class ConcurrencyAsyncTests { + + /** + * FIXME: make the user executor configurable in com.arangodb.internal.ArangoExecutorAsync::execute + * (eg. this test passes using a CachedThreadPool) + */ + @Disabled + @ParameterizedTest + @EnumSource(Protocol.class) + @Timeout(2) + void executorLimit(Protocol protocol) { + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ConfigUtils.loadConfig()) + .maxConnections(1) + .protocol(protocol).build().async(); + + List> futures = IntStream.range(0, 20) + .mapToObj(i -> adb.getVersion() + .whenComplete((dbVersion, ex) -> { + System.out.println(Thread.currentThread().getName()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + })) + .collect(Collectors.toList()); + + futures.forEach(future -> { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + fail(); + } + }); + adb.shutdown(); + } + + + @Disabled + @ParameterizedTest + @EnumSource(Protocol.class) + @Timeout(1) + void outgoingRequestsParallelismTest(Protocol protocol) { + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ConfigUtils.loadConfig()) + .protocol(protocol).build().async(); + + for (int i = 0; i < 50_000; i++) { + adb.getVersion(); + } + adb.shutdown(); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void concurrentPendingRequests(Protocol protocol) throws ExecutionException, InterruptedException { + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ConfigUtils.loadConfig()) + .protocol(protocol).build().async(); + ExecutorService es = Executors.newFixedThreadPool(10); + List> futures = IntStream.range(0, 10) + .mapToObj(__ -> CompletableFuture.runAsync(() -> adb.db().query("RETURN SLEEP(1)", Void.class), es)) + .collect(Collectors.toList()); + for (CompletableFuture f : futures) { + f.get(); + } + adb.shutdown(); + es.shutdown(); + } + +} diff --git a/driver/src/test/java/com/arangodb/InvertedIndexAsyncTest.java b/driver/src/test/java/com/arangodb/InvertedIndexAsyncTest.java new file mode 100644 index 000000000..2bfb21c81 --- /dev/null +++ b/driver/src/test/java/com/arangodb/InvertedIndexAsyncTest.java @@ -0,0 +1,209 @@ +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.entity.arangosearch.*; +import com.arangodb.entity.arangosearch.analyzer.DelimiterAnalyzer; +import com.arangodb.entity.arangosearch.analyzer.DelimiterAnalyzerProperties; +import com.arangodb.model.InvertedIndexOptions; +import com.arangodb.model.PersistentIndexOptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class InvertedIndexAsyncTest extends BaseJunit5 { + + private static final String COLLECTION_NAME = "InvertedIndexTest_collection"; + + private static Stream asyncCols() { + return asyncDbsStream().map(db -> db.collection(COLLECTION_NAME)).map(Arguments::of); + } + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + } + + private void createAnalyzer(String analyzerName, ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + Set features = new HashSet<>(); + features.add(AnalyzerFeature.frequency); + features.add(AnalyzerFeature.norm); + features.add(AnalyzerFeature.position); + + DelimiterAnalyzer da = new DelimiterAnalyzer(); + da.setName(analyzerName); + da.setFeatures(features); + DelimiterAnalyzerProperties props = new DelimiterAnalyzerProperties(); + props.setDelimiter("-"); + da.setProperties(props); + + db.createSearchAnalyzer(da).get(); + } + + private InvertedIndexOptions createOptions(String analyzerName) { + Boolean cache = isEnterprise() ? true : null; + Boolean fieldCache = cache != null ? false : null; + InvertedIndexField field = new InvertedIndexField() + .name("foo") + .analyzer(AnalyzerType.identity.toString()) + .includeAllFields(true) + .searchField(false) + .trackListPositions(false) + .cache(fieldCache) + .features( + AnalyzerFeature.position, + AnalyzerFeature.frequency, + AnalyzerFeature.norm, + AnalyzerFeature.offset + ); + + if (isEnterprise()) { + field.nested( + new InvertedIndexField() + .name("bar") + .analyzer(analyzerName) + .searchField(true) + .features(AnalyzerFeature.position, AnalyzerFeature.frequency) + .nested( + new InvertedIndexField() + .name("baz") + .analyzer(AnalyzerType.identity.toString()) + .searchField(false) + .features(AnalyzerFeature.frequency) + ) + ); + } + + return new InvertedIndexOptions() + .name(rndName()) + .inBackground(true) + .parallelism(5) + .primarySort(new InvertedIndexPrimarySort() + .fields( + new InvertedIndexPrimarySort.Field("f1", InvertedIndexPrimarySort.Field.Direction.asc), + new InvertedIndexPrimarySort.Field("f2", InvertedIndexPrimarySort.Field.Direction.desc) + ) + .compression(ArangoSearchCompression.lz4) + .cache(cache) + ) + .storedValues(new StoredValue(Arrays.asList("f3", "f4"), ArangoSearchCompression.none, cache)) + .optimizeTopK("BM25(@doc) DESC", "TFIDF(@doc) DESC") + .analyzer(analyzerName) + .features(AnalyzerFeature.position, AnalyzerFeature.frequency) + .includeAllFields(false) + .trackListPositions(true) + .searchField(true) + .fields(field) + .consolidationIntervalMsec(11L) + .commitIntervalMsec(22L) + .cleanupIntervalStep(33L) + .consolidationPolicy(ConsolidationPolicy.of(ConsolidationType.TIER) + .segmentsMin(3L) + .segmentsMax(44L) + .segmentsBytesMax(55555L) + .segmentsBytesFloor(666L) + .minScore(77L) + ) + .writebufferIdle(44L) + .writebufferActive(55L) + .writebufferSizeMax(66L) + .cache(cache) + .primaryKeyCache(cache); + } + + private void assertCorrectIndexEntity(InvertedIndexEntity indexResult, InvertedIndexOptions options) { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getId()).isNotNull().isNotEmpty(); + // FIXME: in single server this is null + // assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getSparse()).isTrue(); + assertThat(indexResult.getVersion()).isNotNull(); + assertThat(indexResult.getCode()).isNotNull(); + assertThat(indexResult.getType()).isEqualTo(IndexType.inverted); + assertThat(indexResult.getName()).isEqualTo(options.getName()); + assertThat(indexResult.getFields()).containsExactlyElementsOf(options.getFields()); + assertThat(indexResult.getSearchField()).isEqualTo(options.getSearchField()); + assertThat(indexResult.getStoredValues()).containsExactlyElementsOf(options.getStoredValues()); + assertThat(indexResult.getPrimarySort()).isEqualTo(options.getPrimarySort()); + assertThat(indexResult.getAnalyzer()).isEqualTo(options.getAnalyzer()); + assertThat(indexResult.getFeatures()).hasSameElementsAs(options.getFeatures()); + assertThat(indexResult.getIncludeAllFields()).isEqualTo(options.getIncludeAllFields()); + assertThat(indexResult.getTrackListPositions()).isEqualTo(options.getTrackListPositions()); + assertThat(indexResult.getCleanupIntervalStep()).isEqualTo(options.getCleanupIntervalStep()); + assertThat(indexResult.getCommitIntervalMsec()).isEqualTo(options.getCommitIntervalMsec()); + assertThat(indexResult.getConsolidationIntervalMsec()).isEqualTo(options.getConsolidationIntervalMsec()); + assertThat(indexResult.getConsolidationPolicy()).isEqualTo(options.getConsolidationPolicy()); + assertThat(indexResult.getWritebufferIdle()).isEqualTo(options.getWritebufferIdle()); + assertThat(indexResult.getWritebufferActive()).isEqualTo(options.getWritebufferActive()); + assertThat(indexResult.getWritebufferSizeMax()).isEqualTo(options.getWritebufferSizeMax()); + assertThat(indexResult.getCache()).isEqualTo(options.getCache()); + assertThat(indexResult.getPrimaryKeyCache()).isEqualTo(options.getPrimaryKeyCache()); + + if (isEnterprise() && isAtLeastVersion(3, 11)) { + // FIXME: BTS-1428 + // assertThat(indexResult.getOptimizeTopK()).containsExactlyElementsOf(options.getOptimizeTopK()); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void createAndGetInvertedIndex(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options).get(); + assertCorrectIndexEntity(created, options); + InvertedIndexEntity loadedIndex = collection.getInvertedIndex(created.getName()).get(); + assertCorrectIndexEntity(loadedIndex, options); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getInvertedIndexesShouldNotReturnOtherIndexTypes(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + // create persistent index + collection.ensurePersistentIndex(Collections.singletonList("foo"), new PersistentIndexOptions().name("persistentIndex")); + + // create inverted index + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options).get(); + + Collection loadedIndexes = collection.getInvertedIndexes().get(); + assertThat(loadedIndexes).map(InvertedIndexEntity::getName) + .doesNotContain("persistentIndex") + .contains(created.getName()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncCols") + void getIndexesShouldNotReturnInvertedIndexes(ArangoCollectionAsync collection) throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + // create persistent index + collection.ensurePersistentIndex(Collections.singletonList("foo"), new PersistentIndexOptions().name("persistentIndex")); + + // create inverted index + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options).get(); + + Collection loadedIndexes = collection.getIndexes().get(); + assertThat(loadedIndexes).map(IndexEntity::getName) + .doesNotContain(created.getName()) + .contains("persistentIndex"); + } + +} diff --git a/driver/src/test/java/com/arangodb/JwtAuthAsyncTest.java b/driver/src/test/java/com/arangodb/JwtAuthAsyncTest.java new file mode 100644 index 000000000..cbeb68c9f --- /dev/null +++ b/driver/src/test/java/com/arangodb/JwtAuthAsyncTest.java @@ -0,0 +1,101 @@ +package com.arangodb; + +import com.arangodb.config.ConfigUtils; +import com.arangodb.internal.ArangoRequestParam; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Michele Rastelli + */ +class JwtAuthAsyncTest { + + private volatile static String jwt; + + @BeforeAll + static void init() { + ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(ConfigUtils.loadConfig()) + .build(); + jwt = getJwt(arangoDB); + arangoDB.shutdown(); + } + + private static String getJwt(ArangoDB arangoDB) { + Map reqBody = new HashMap<>(); + reqBody.put("username", "root"); + reqBody.put("password", "test"); + + Request req = Request.builder() + .db(ArangoRequestParam.SYSTEM) + .method(Request.Method.POST) + .path("/_open/auth") + .body(reqBody) + .build(); + + Response resp = arangoDB.execute(req, Map.class); + return (String) resp.getBody().get("jwt"); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void notAuthenticated(Protocol protocol) { + ArangoDBAsync arangoDB = getBuilder(protocol).build().async(); + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(401); + arangoDB.shutdown(); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void authenticated(Protocol protocol) throws ExecutionException, InterruptedException { + ArangoDBAsync arangoDB = getBuilder(protocol) + .jwt(jwt) + .build() + .async(); + arangoDB.getVersion().get(); + arangoDB.shutdown(); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void updateJwt(Protocol protocol) throws ExecutionException, InterruptedException { + assumeTrue(protocol != Protocol.VST, "DE-423"); + ArangoDBAsync arangoDB = getBuilder(protocol) + .jwt(jwt) + .build() + .async(); + arangoDB.getVersion().get(); + arangoDB.updateJwt("bla"); + + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(401); + + arangoDB.updateJwt(jwt); + arangoDB.getVersion().get(); + arangoDB.shutdown(); + } + + private ArangoDB.Builder getBuilder(Protocol protocol) { + return new ArangoDB.Builder() + .loadProperties(ConfigUtils.loadConfig()) + .protocol(protocol) + .jwt(null) // unset credentials from properties file + .user(null) // unset credentials from properties file + .password(null); // unset credentials from properties file + } +} diff --git a/driver/src/test/java/com/arangodb/ParallelAsyncTest.java b/driver/src/test/java/com/arangodb/ParallelAsyncTest.java new file mode 100644 index 000000000..df72b33c9 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ParallelAsyncTest.java @@ -0,0 +1,39 @@ +package com.arangodb; + +import com.arangodb.config.ConfigUtils; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +class ParallelAsyncTest { + + @ParameterizedTest(name = "{index}") + @EnumSource(Protocol.class) + void connectionParallelism(Protocol protocol) throws InterruptedException { + // test that connections are internally async and can have multiple pending requests + // BTS-1102: the server does not run pipelined HTTP/1.1 requests in parallel + assumeTrue(protocol != Protocol.HTTP_JSON && protocol != Protocol.HTTP_VPACK); + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ConfigUtils.loadConfig()) + .protocol(protocol) + .maxConnections(1) + .build() + .async(); + + List> tasks = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + tasks.add(adb.db().query("return sleep(1)", Void.class)); + } + + Thread.sleep(2_000); + assertThat(tasks).allMatch(Future::isDone); + adb.shutdown(); + } + +} diff --git a/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java b/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java index b9da09db1..39ad36477 100644 --- a/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/StreamTransactionAsyncTest.java @@ -26,10 +26,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -675,67 +672,70 @@ void truncate(ArangoDatabaseAsync db) throws ExecutionException, InterruptedExce assertThat(collection.count().get().getCount()).isZero(); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void createCursor(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { -// assumeTrue(isSingleServer()); -// assumeTrue(isAtLeastVersion(3, 5)); -// assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); -// -// StreamTransactionEntity tx = db -// .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)).get(); -// ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); -// -// // insert a document from outside the tx -// DocumentCreateEntity externalDoc = collection -// .insertDocument(new BaseDocument(), null).get(); -// -// final Map bindVars = new HashMap<>(); -// bindVars.put("@collection", COLLECTION_NAME); -// bindVars.put("key", externalDoc.getKey()); -// -// ArangoCursor cursor = db -// .query("FOR doc IN @@collection FILTER doc._key == @key RETURN doc", BaseDocument.class, bindVars, -// new AqlQueryOptions().streamTransactionId(tx.getId())); -// -// // assert that the document is not found from within the tx -// assertThat(cursor.hasNext()).isFalse(); -// -// db.abortStreamTransaction(tx.getId()); -// } -// -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void nextCursor(ArangoDatabaseAsync db) { -// assumeTrue(isSingleServer()); -// assumeTrue(isAtLeastVersion(3, 5)); -// assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); -// -// StreamTransactionEntity tx = db.beginStreamTransaction( -// new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); -// ArangoCollection collection = db.collection(COLLECTION_NAME); -// -// // insert documents from within the tx -// List keys = collection -// .insertDocuments(IntStream.range(0, 10).mapToObj(it -> new BaseDocument()).collect(Collectors.toList()), -// new DocumentCreateOptions().streamTransactionId(tx.getId())).getDocuments().stream() -// .map(DocumentEntity::getKey).collect(Collectors.toList()); -// -// final Map bindVars = new HashMap<>(); -// bindVars.put("@collection", COLLECTION_NAME); -// bindVars.put("keys", keys); -// -// ArangoCursor cursor = db -// .query("FOR doc IN @@collection FILTER CONTAINS_ARRAY(@keys, doc._key) RETURN doc", BaseDocument.class, bindVars, -// new AqlQueryOptions().streamTransactionId(tx.getId()).batchSize(2)); -// -// List docs = cursor.asListRemaining(); -// -// // assert that all the keys are returned from the query -// assertThat(docs.stream().map(BaseDocument::getKey).collect(Collectors.toList())).containsAll(keys); -// -// db.abortStreamTransaction(tx.getId()); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void createCursor(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)).get(); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = collection + .insertDocument(new BaseDocument(), null).get(); + + final Map bindVars = new HashMap<>(); + bindVars.put("@collection", COLLECTION_NAME); + bindVars.put("key", externalDoc.getKey()); + + ArangoCursorAsync cursor = db + .query("FOR doc IN @@collection FILTER doc._key == @key RETURN doc", BaseDocument.class, bindVars, + new AqlQueryOptions().streamTransactionId(tx.getId())).get(); + + // assert that the document is not found from within the tx + assertThat(cursor.getResult()).isEmpty(); + + db.abortStreamTransaction(tx.getId()).get(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void nextCursor(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)).get(); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // insert documents from within the tx + List keys = collection + .insertDocuments(IntStream.range(0, 10).mapToObj(it -> new BaseDocument()).collect(Collectors.toList()), + new DocumentCreateOptions().streamTransactionId(tx.getId())).get().getDocuments().stream() + .map(DocumentEntity::getKey).collect(Collectors.toList()); + + final Map bindVars = new HashMap<>(); + bindVars.put("@collection", COLLECTION_NAME); + bindVars.put("keys", keys); + + ArangoCursorAsync cursor = db + .query("FOR doc IN @@collection FILTER CONTAINS_ARRAY(@keys, doc._key) RETURN doc", BaseDocument.class, bindVars, + new AqlQueryOptions().streamTransactionId(tx.getId()).batchSize(2)).get(); + + List docs = new ArrayList<>(cursor.getResult()); + while (cursor.hasMore()) { + cursor = cursor.nextBatch().get(); + docs.addAll(cursor.getResult()); + } + + // assert that all the keys are returned from the query + assertThat(docs.stream().map(BaseDocument::getKey).collect(Collectors.toList())).containsAll(keys); + db.abortStreamTransaction(tx.getId()); + } @ParameterizedTest(name = "{index}") @MethodSource("asyncDbs") @@ -786,34 +786,33 @@ void transactionAllowImplicitFalse(ArangoDatabaseAsync db) throws ExecutionExcep db.abortStreamTransaction(tx.getId()).get(); } -// @ParameterizedTest(name = "{index}") -// @MethodSource("asyncDbs") -// void transactionDirtyRead(ArangoDatabaseAsync db) throws IOException, ExecutionException, InterruptedException { -// assumeTrue(isCluster()); -// assumeTrue(isAtLeastVersion(3, 10)); -// -// ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); -// DocumentCreateEntity doc = collection.insertDocument(new BaseDocument()).get(); -// -// StreamTransactionEntity tx = db -// .beginStreamTransaction(new StreamTransactionOptions() -// .readCollections(COLLECTION_NAME) -// .allowDirtyRead(true)).get(); -// -// MultiDocumentEntity readDocs = collection.getDocuments(Collections.singletonList(doc.getKey()), -// BaseDocument.class, -// new DocumentReadOptions().streamTransactionId(tx.getId())).get(); -// -// assertThat(readDocs.isPotentialDirtyRead()).isTrue(); -// assertThat(readDocs.getDocuments()).hasSize(1); -// -// final ArangoCursor cursor = db.query("FOR i IN @@col RETURN i", BaseDocument.class, -// Collections.singletonMap("@col", COLLECTION_NAME), -// new AqlQueryOptions().streamTransactionId(tx.getId())); -// assertThat(cursor.isPotentialDirtyRead()).isTrue(); -// cursor.close(); -// -// db.abortStreamTransaction(tx.getId()); -// } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncDbs") + void transactionDirtyRead(ArangoDatabaseAsync db) throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + assumeTrue(isAtLeastVersion(3, 10)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + DocumentCreateEntity doc = collection.insertDocument(new BaseDocument()).get(); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions() + .readCollections(COLLECTION_NAME) + .allowDirtyRead(true)).get(); + + MultiDocumentEntity readDocs = collection.getDocuments(Collections.singletonList(doc.getKey()), + BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get(); + + assertThat(readDocs.isPotentialDirtyRead()).isTrue(); + assertThat(readDocs.getDocuments()).hasSize(1); + + final ArangoCursorAsync cursor = db.query("FOR i IN @@col RETURN i", BaseDocument.class, + Collections.singletonMap("@col", COLLECTION_NAME), + new AqlQueryOptions().streamTransactionId(tx.getId())).get(); + assertThat(cursor.isPotentialDirtyRead()).isTrue(); + + db.abortStreamTransaction(tx.getId()).get(); + } } diff --git a/driver/src/test/java/com/arangodb/UserAgentAsyncTest.java b/driver/src/test/java/com/arangodb/UserAgentAsyncTest.java new file mode 100644 index 000000000..605109054 --- /dev/null +++ b/driver/src/test/java/com/arangodb/UserAgentAsyncTest.java @@ -0,0 +1,43 @@ +package com.arangodb; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + +class UserAgentAsyncTest extends BaseJunit5 { + + private static final String EXPECTED_VERSION = "7.2.0-SNAPSHOT"; + + @Test + void packageVersion() { + assertThat(PackageVersion.VERSION).isEqualTo(EXPECTED_VERSION); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void userAgentHeader(Protocol protocol) throws ExecutionException, InterruptedException { + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(config) + .protocol(protocol) + .build() + .async(); + + Response resp = adb.execute(Request.builder() + .method(Request.Method.GET) + .path("/_admin/echo") + .build(), JsonNode.class) + .get(); + String headerValue = resp.getBody().get("headers").get("x-arango-driver").textValue(); + + String jvmVersion = System.getProperty("java.specification.version"); + String expected = "JavaDriver/" + PackageVersion.VERSION + " (JVM/" + jvmVersion + ")"; + + assertThat(headerValue).isEqualTo(expected); + adb.shutdown(); + } +} diff --git a/driver/src/test/java/perf/SimpleAsyncPerfTest.java b/driver/src/test/java/perf/SimpleAsyncPerfTest.java new file mode 100644 index 000000000..16cf43cfc --- /dev/null +++ b/driver/src/test/java/perf/SimpleAsyncPerfTest.java @@ -0,0 +1,79 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package perf; + +import com.arangodb.ArangoDB; +import com.arangodb.ArangoDBAsync; +import com.arangodb.Protocol; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Michele Rastelli + */ +@Disabled +class SimpleAsyncPerfTest { + private static final int TARGET = 500_000; + private static final int MAX_PENDING_REQUESTS = 500; + + private void doGetVersion(ArangoDBAsync arangoDB) throws InterruptedException { + AtomicInteger pendingReqsCount = new AtomicInteger(); + AtomicInteger completed = new AtomicInteger(); + + while (completed.get() < TARGET) { + pendingReqsCount.incrementAndGet(); + arangoDB.getVersion() + .thenAccept(it -> { + pendingReqsCount.decrementAndGet(); + completed.incrementAndGet(); + }); + while (pendingReqsCount.get() > MAX_PENDING_REQUESTS) { + Thread.sleep(5); + } + } + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void getVersion(Protocol protocol) throws InterruptedException { + ArangoDBAsync arangoDB = new ArangoDB.Builder() + .host("172.28.0.1", 8529) + .password("test") + .protocol(protocol) + .build() + .async(); + // warmup + doGetVersion(arangoDB); + + long start = new Date().getTime(); + doGetVersion(arangoDB); + long end = new Date().getTime(); + long elapsedMs = end - start; + System.out.println("elapsed ms: " + elapsedMs); + long reqPerSec = TARGET * 1_000 / elapsedMs; + System.out.println("req/s: " + reqPerSec); + System.out.println("---"); + } +} From 04eda9f4345626d6b821eddab1bd3b3b2a42d172 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 24 Oct 2023 09:58:14 +0200 Subject: [PATCH 51/62] CustomSerdeAsyncTest --- .../arangodb/serde/CustomSerdeAsyncTest.java | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 driver/src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java diff --git a/driver/src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java b/driver/src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java new file mode 100644 index 000000000..d275849d3 --- /dev/null +++ b/driver/src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java @@ -0,0 +1,244 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb.serde; + + +import com.arangodb.*; +import com.arangodb.config.ConfigUtils; +import com.arangodb.internal.serde.InternalSerde; +import com.arangodb.model.DocumentCreateOptions; +import com.arangodb.serde.jackson.JacksonSerde; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static com.fasterxml.jackson.databind.DeserializationFeature.USE_BIG_INTEGER_FOR_INTS; +import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED; +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Michele Rastelli + */ +class CustomSerdeAsyncTest { + + private static final String COLLECTION_NAME = "collection"; + private static final String PERSON_SERIALIZER_ADDED_PREFIX = "MyNameIs"; + private static final String PERSON_DESERIALIZER_ADDED_PREFIX = "Hello"; + + private static ArangoDBAsync arangoDB; + private static ArangoDatabaseAsync db; + private static ArangoCollectionAsync collection; + + @BeforeAll + static void init() throws ExecutionException, InterruptedException { + JacksonSerde serde = JacksonSerde.of(ContentType.JSON) + .configure((mapper) -> { + mapper.configure(WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true); + mapper.configure(USE_BIG_INTEGER_FOR_INTS, true); + SimpleModule module = new SimpleModule("PersonModule"); + module.addDeserializer(Person.class, new PersonDeserializer()); + mapper.registerModule(module); + }); + arangoDB = new ArangoDB.Builder() + .loadProperties(ConfigUtils.loadConfig()) + .serde(serde) + .protocol(Protocol.HTTP_JSON) + .build() + .async(); + + db = arangoDB.db("custom-serde-test"); + if (!db.exists().get()) { + db.create().get(); + } + + collection = db.collection(COLLECTION_NAME); + if (!collection.exists().get()) { + collection.create().get(); + } + } + + @AfterAll + static void shutdown() throws ExecutionException, InterruptedException { + if (db.exists().get()) + db.drop().get(); + } + + @Test + void customPersonDeserializer() throws ExecutionException, InterruptedException { + Person person = new Person(); + person.name = "Joe"; + Person result = collection.insertDocument( + person, + new DocumentCreateOptions().returnNew(true) + ).get().getNew(); + assertThat(result.name).isEqualTo(PERSON_DESERIALIZER_ADDED_PREFIX + PERSON_SERIALIZER_ADDED_PREFIX + person.name); + } + + @Test + void manualCustomPersonDeserializer() { + Person person = new Person(); + person.name = "Joe"; + InternalSerde serialization = arangoDB.getSerde(); + byte[] serialized = serialization.serializeUserData(person); + Person deserializedPerson = serialization.deserializeUserData(serialized, Person.class); + assertThat(deserializedPerson.name).isEqualTo(PERSON_DESERIALIZER_ADDED_PREFIX + PERSON_SERIALIZER_ADDED_PREFIX + person.name); + } + + @Test + void aqlSerialization() throws ExecutionException, InterruptedException { + String key = "test-" + UUID.randomUUID(); + + Map doc = new HashMap<>(); + doc.put("_key", key); + doc.put("arr", Collections.singletonList("hello")); + doc.put("int", 10); + + HashMap params = new HashMap<>(); + params.put("doc", doc); + params.put("@collection", COLLECTION_NAME); + + Map result = db.query( + "INSERT @doc INTO @@collection RETURN NEW", + Map.class, + params + ).get().getResult().get(0); + + assertThat(result.get("arr")).isInstanceOf(String.class); + assertThat(result.get("arr")).isEqualTo("hello"); + assertThat(result.get("int")).isInstanceOf(BigInteger.class); + assertThat(result.get("int")).isEqualTo(BigInteger.valueOf(10)); + } + + @Test + void aqlDeserialization() throws ExecutionException, InterruptedException { + String key = "test-" + UUID.randomUUID(); + + Map doc = new HashMap<>(); + doc.put("_key", key); + doc.put("arr", Collections.singletonList("hello")); + doc.put("int", 10); + + collection.insertDocument(doc); + + final Map result = db.query( + "RETURN DOCUMENT(@docId)", + Map.class, + Collections.singletonMap("docId", COLLECTION_NAME + "/" + key) + ).get().getResult().get(0); + + assertThat(result.get("arr")).isInstanceOf(String.class); + assertThat(result.get("arr")).isEqualTo("hello"); + assertThat(result.get("int")).isInstanceOf(BigInteger.class); + assertThat(result.get("int")).isEqualTo(BigInteger.valueOf(10)); + } + + @Test + void insertDocument() throws ExecutionException, InterruptedException { + String key = "test-" + UUID.randomUUID(); + + Map doc = new HashMap<>(); + doc.put("_key", key); + doc.put("arr", Collections.singletonList("hello")); + doc.put("int", 10); + + Map result = collection.insertDocument( + doc, + new DocumentCreateOptions().returnNew(true) + ).get().getNew(); + + assertThat(result.get("arr")).isInstanceOf(String.class); + assertThat(result.get("arr")).isEqualTo("hello"); + assertThat(result.get("int")).isInstanceOf(BigInteger.class); + assertThat(result.get("int")).isEqualTo(BigInteger.valueOf(10)); + } + + @Test + void getDocument() throws ExecutionException, InterruptedException { + String key = "test-" + UUID.randomUUID(); + + Map doc = new HashMap<>(); + doc.put("_key", key); + doc.put("arr", Collections.singletonList("hello")); + doc.put("int", 10); + + collection.insertDocument(doc).get(); + + final Map result = db.collection(COLLECTION_NAME).getDocument( + key, + Map.class, + null).get(); + + assertThat(result.get("arr")).isInstanceOf(String.class); + assertThat(result.get("arr")).isEqualTo("hello"); + assertThat(result.get("int")).isInstanceOf(BigInteger.class); + assertThat(result.get("int")).isEqualTo(BigInteger.valueOf(10)); + } + + @Test + void parseNullString() { + final String json = arangoDB.getSerde().deserializeUserData(arangoDB.getSerde().serializeUserData(null), + String.class); + assertThat(json).isNull(); + } + + static class PersonSerializer extends JsonSerializer { + @Override + public void serialize(Person value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("name"); + gen.writeString(PERSON_SERIALIZER_ADDED_PREFIX + value.name); + gen.writeEndObject(); + } + } + + static class PersonDeserializer extends JsonDeserializer { + @Override + public Person deserialize(JsonParser parser, DeserializationContext ctx) throws IOException { + Person person = new Person(); + JsonNode rootNode = parser.getCodec().readTree(parser); + JsonNode nameNode = rootNode.get("name"); + if (nameNode != null && nameNode.isTextual()) { + person.name = PERSON_DESERIALIZER_ADDED_PREFIX + nameNode.asText(); + } + return person; + } + } + + @JsonSerialize(using = PersonSerializer.class) + static class Person { + String name; + } + +} From b73668ebac29ab8085072d887efab0ec204d8e19 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 24 Oct 2023 10:19:13 +0200 Subject: [PATCH 52/62] fix native tests --- .../arangodb-java-driver/reflect-config.json | 12 +++++++++ .../META-INF/native-image/reflect-config.json | 25 +++++++++++++++++++ .../reflect-config.json | 12 +++++++++ 3 files changed, 49 insertions(+) diff --git a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config.json b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config.json index 3325fca05..b0ed104a7 100644 --- a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config.json +++ b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config.json @@ -1534,5 +1534,17 @@ "allDeclaredFields": true, "allDeclaredMethods": true, "allDeclaredConstructors": true + }, + { + "name": "com.arangodb.entity.CursorEntity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.arangodb.entity.CursorEntity$Extra", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true } ] diff --git a/driver/src/test/resources/META-INF/native-image/reflect-config.json b/driver/src/test/resources/META-INF/native-image/reflect-config.json index 9e4087f00..220895362 100644 --- a/driver/src/test/resources/META-INF/native-image/reflect-config.json +++ b/driver/src/test/resources/META-INF/native-image/reflect-config.json @@ -121,6 +121,31 @@ } ] }, + { + "name": "com.arangodb.serde.CustomSerdeAsyncTest$Person", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.serde.CustomSerdeAsyncTest$PersonDeserializer", + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true + }, + { + "name": "com.arangodb.serde.CustomSerdeAsyncTest$PersonSerializer", + "allDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, { "name": "com.arangodb.serde.CustomTypeHintTest$Animal", "allDeclaredMethods": true, diff --git a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config.json b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config.json index 3325fca05..b0ed104a7 100644 --- a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config.json +++ b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config.json @@ -1534,5 +1534,17 @@ "allDeclaredFields": true, "allDeclaredMethods": true, "allDeclaredConstructors": true + }, + { + "name": "com.arangodb.entity.CursorEntity", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true + }, + { + "name": "com.arangodb.entity.CursorEntity$Extra", + "allDeclaredFields": true, + "allDeclaredMethods": true, + "allDeclaredConstructors": true } ] From 55e8be4b4439a7895b80391e62eb6613f87acf5a Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 24 Oct 2023 15:42:22 +0200 Subject: [PATCH 53/62] [DE-528] non-blocking acquireHostList (#521) * HostResolver refactoring * HostResolver refactoring * HostResolver refactoring * HostResolver refactoring * HostResolver refactoring * HostResolver refactoring * reverted acquireHostList=true in tests * VST outgoing executor (DE-696) --- core/src/main/java/com/arangodb/ArangoDB.java | 8 +++- .../java/com/arangodb/ArangoDBException.java | 7 ++- .../com/arangodb/internal/ArangoDBImpl.java | 8 ++-- .../arangodb/internal/ArangoExecuteable.java | 6 +-- .../arangodb/internal/InternalArangoDB.java | 4 +- .../internal/InternalArangoDBBuilder.java | 3 +- .../internal/net/DirtyReadHostHandler.java | 15 ------- .../internal/net/ExtendedHostResolver.java | 43 ++++++++++--------- .../internal/net/FallbackHostHandler.java | 28 ++---------- .../arangodb/internal/net/HostHandler.java | 6 --- .../arangodb/internal/net/HostResolver.java | 8 +++- .../internal/net/RandomHostHandler.java | 26 +++-------- .../internal/net/RoundRobinHostHandler.java | 19 ++------ .../internal/net/SimpleHostResolver.java | 10 +---- .../java/com/arangodb/ArangoDBAsyncTest.java | 16 ++++--- .../test/java/com/arangodb/ArangoDBTest.java | 16 ++++--- .../java/com/arangodb/JwtAuthAsyncTest.java | 2 +- .../test/java/com/arangodb/JwtAuthTest.java | 2 +- .../arangodb/internal/HostHandlerTest.java | 43 +++++-------------- .../resources/arangodb-with-prefix.properties | 2 +- driver/src/test/resources/arangodb.properties | 4 +- .../com/arangodb/http/HttpCommunication.java | 1 - .../com/arangodb/vst/VstCommunication.java | 1 - .../java/com/arangodb/vst/VstProtocol.java | 7 ++- 24 files changed, 104 insertions(+), 181 deletions(-) diff --git a/core/src/main/java/com/arangodb/ArangoDB.java b/core/src/main/java/com/arangodb/ArangoDB.java index 47535af5d..57552df9f 100644 --- a/core/src/main/java/com/arangodb/ArangoDB.java +++ b/core/src/main/java/com/arangodb/ArangoDB.java @@ -22,6 +22,7 @@ import com.arangodb.entity.*; import com.arangodb.internal.ArangoDBImpl; +import com.arangodb.internal.ArangoExecutorSync; import com.arangodb.internal.InternalArangoDBBuilder; import com.arangodb.internal.net.*; import com.arangodb.model.*; @@ -363,10 +364,13 @@ public ArangoDB build() { HostHandler hostHandler = createHostHandler(hostResolver); hostHandler.setJwt(config.getJwt()); + CommunicationProtocol protocol = protocolProvider.createProtocol(config, hostHandler); + ArangoExecutorSync executor = new ArangoExecutorSync(protocol, config); + hostResolver.init(executor, config.getInternalSerde()); + return new ArangoDBImpl( config, - hostResolver, - protocolProvider, + protocol, hostHandler ); } diff --git a/core/src/main/java/com/arangodb/ArangoDBException.java b/core/src/main/java/com/arangodb/ArangoDBException.java index 9bc9a47fe..54fed3496 100644 --- a/core/src/main/java/com/arangodb/ArangoDBException.java +++ b/core/src/main/java/com/arangodb/ArangoDBException.java @@ -39,7 +39,7 @@ public ArangoDBException(final ErrorEntity errorEntity) { super(String.format("Response: %s, Error: %s - %s", errorEntity.getCode(), errorEntity.getErrorNum(), errorEntity.getErrorMessage())); this.entity = errorEntity; - this.responseCode = null; + this.responseCode = errorEntity.getCode(); this.requestId = null; } @@ -117,6 +117,9 @@ public static ArangoDBException of(Throwable t, Long requestId) { private static ArangoDBException of(String message, Throwable t, Long requestId) { Objects.requireNonNull(t); + if (t instanceof CompletionException) { + return of(message, t.getCause(), requestId); + } Throwable cause = unwrapCause(t); String msg = message != null ? message : t.getMessage() != null ? t.getMessage() @@ -141,7 +144,7 @@ private static ArangoDBException of(String message, Throwable t, Long requestId) } private static Throwable unwrapCause(Throwable t) { - if (t instanceof ArangoDBException || t instanceof CompletionException) { + if (t instanceof ArangoDBException) { return unwrapCause(t.getCause()); } return t; diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java index ad3321c24..7554c9919 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBImpl.java @@ -23,9 +23,8 @@ import com.arangodb.*; import com.arangodb.entity.*; import com.arangodb.internal.config.ArangoConfig; +import com.arangodb.internal.net.CommunicationProtocol; import com.arangodb.internal.net.HostHandler; -import com.arangodb.internal.net.HostResolver; -import com.arangodb.internal.net.ProtocolProvider; import com.arangodb.internal.serde.SerdeUtils; import com.arangodb.model.*; import org.slf4j.Logger; @@ -44,11 +43,10 @@ public class ArangoDBImpl extends InternalArangoDB implements ArangoDB { private final HostHandler hostHandler; public ArangoDBImpl(final ArangoConfig config, - final HostResolver hostResolver, final ProtocolProvider protocolProvider, + final CommunicationProtocol protocol, final HostHandler hostHandler) { - super(protocolProvider.createProtocol(config, hostHandler), config, config.getInternalSerde()); + super(protocol, config); this.hostHandler = hostHandler; - hostResolver.init(executorSync(), getSerde()); LOGGER.debug("ArangoDB Client is ready to use"); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java b/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java index 31e68e043..db67992e2 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecuteable.java @@ -37,10 +37,8 @@ public abstract class ArangoExecuteable implements ArangoSerdeAccessor { private final ArangoExecutorAsync executorAsync; private final InternalSerde serde; - protected ArangoExecuteable(final CommunicationProtocol protocol, - final ArangoConfig config, - final InternalSerde serde) { - this(new ArangoExecutorSync(protocol, config), new ArangoExecutorAsync(protocol, config), serde); + protected ArangoExecuteable(final CommunicationProtocol protocol, final ArangoConfig config) { + this(new ArangoExecutorSync(protocol, config), new ArangoExecutorAsync(protocol, config), config.getInternalSerde()); } protected ArangoExecuteable(final ArangoExecuteable other) { diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java index 65725511c..a8159c1f7 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java @@ -50,8 +50,8 @@ public abstract class InternalArangoDB extends ArangoExecuteable { private static final String PATH_API_USER = "/_api/user"; private static final String PATH_API_QUERY_RULES = "/_api/query/rules"; - protected InternalArangoDB(final CommunicationProtocol protocol, final ArangoConfig config, final InternalSerde util) { - super(protocol, config, util); + protected InternalArangoDB(final CommunicationProtocol protocol, final ArangoConfig config) { + super(protocol, config); } protected InternalArangoDB(final ArangoExecuteable other) { diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java b/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java index c406e78d9..13b73bb99 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java @@ -337,10 +337,9 @@ protected HostResolver createHostResolver(final Collection hosts, final Co LOG.debug("Use SimpleHostResolver"); return new SimpleHostResolver(new ArrayList<>(hosts)); } - } - protected Collection createHostList(final ConnectionFactory connectionFactory) { + protected Collection createHostList(final ConnectionFactory connectionFactory) { final Collection hostList = new ArrayList<>(); for (final HostDescription host : config.getHosts()) { hostList.add(HostUtils.createHost(host, config, connectionFactory)); diff --git a/core/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java b/core/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java index 2fded0c99..3826e6de5 100644 --- a/core/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java +++ b/core/src/main/java/com/arangodb/internal/net/DirtyReadHostHandler.java @@ -72,27 +72,12 @@ public void reset() { determineHostHandler().reset(); } - @Override - public void confirm() { - determineHostHandler().confirm(); - } - @Override public void close() throws IOException { master.close(); follower.close(); } - @Override - public void closeCurrentOnError() { - determineHostHandler().closeCurrentOnError(); - } - - @Override - public void closeCurrentOnErrorIfNotMatch(HostDescription host) { - determineHostHandler().closeCurrentOnErrorIfNotMatch(host); - } - @Override public void setJwt(String jwt) { master.setJwt(jwt); diff --git a/core/src/main/java/com/arangodb/internal/net/ExtendedHostResolver.java b/core/src/main/java/com/arangodb/internal/net/ExtendedHostResolver.java index cb62be184..5f778349e 100644 --- a/core/src/main/java/com/arangodb/internal/net/ExtendedHostResolver.java +++ b/core/src/main/java/com/arangodb/internal/net/ExtendedHostResolver.java @@ -33,6 +33,10 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import static com.arangodb.internal.serde.SerdeUtils.constructParametricType; @@ -48,10 +52,10 @@ public class ExtendedHostResolver implements HostResolver { private final ArangoConfig config; private final ConnectionFactory connectionFactory; private final Integer acquireHostListInterval; - private long lastUpdate; + private final ScheduledExecutorService scheduler; private ArangoExecutorSync executor; private InternalSerde arangoSerialization; - + private ScheduledFuture schedule; public ExtendedHostResolver(final List hosts, final ArangoConfig config, final ConnectionFactory connectionFactory, Integer acquireHostListInterval) { @@ -61,22 +65,34 @@ public ExtendedHostResolver(final List hosts, final ArangoConfig config, this.config = config; this.connectionFactory = connectionFactory; - lastUpdate = 0; + scheduler = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = Executors.defaultThreadFactory().newThread(r); + t.setDaemon(true); + return t; + } + ); } @Override public void init(ArangoExecutorSync executor, InternalSerde arangoSerialization) { this.executor = executor; this.arangoSerialization = arangoSerialization; + resolve(); + schedule = scheduler.scheduleAtFixedRate(this::resolve, acquireHostListInterval, acquireHostListInterval, TimeUnit.MILLISECONDS); } @Override - public HostSet resolve(boolean initial, boolean closeConnections) { - - if (!initial && isExpired()) { + public void close() { + schedule.cancel(false); + scheduler.shutdown(); + } - lastUpdate = System.currentTimeMillis(); + @Override + public HostSet getHosts() { + return hosts; + } + private void resolve() { final Collection endpoints = resolveFromServer(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Resolve {} Endpoints", endpoints.size()); @@ -110,17 +126,11 @@ public HostSet resolve(boolean initial, boolean closeConnections) { } } hosts.clearAllMarkedForDeletion(); - } - - return hosts; } private Collection resolveFromServer() { - Collection response; - try { - response = executor.execute( new InternalRequest(ArangoRequestParam.SYSTEM, RequestType.GET, "/_api/cluster/endpoints"), response1 -> { @@ -136,7 +146,6 @@ private Collection resolveFromServer() { }, null); } catch (final ArangoDBException e) { final Integer responseCode = e.getResponseCode(); - // responseCode == 403: single server < 3.7 // responseCode == 501: single server >= 3.7 if (responseCode != null && (responseCode == 403 || responseCode == 501)) { @@ -145,12 +154,6 @@ private Collection resolveFromServer() { throw e; } } - return response; } - - private boolean isExpired() { - return System.currentTimeMillis() > (lastUpdate + acquireHostListInterval); - } - } diff --git a/core/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java b/core/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java index 162adaf86..f0eefcd61 100644 --- a/core/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java +++ b/core/src/main/java/com/arangodb/internal/net/FallbackHostHandler.java @@ -37,16 +37,14 @@ public class FallbackHostHandler implements HostHandler { private Host current; private Host lastSuccess; private int iterations; - private boolean firstOpened; private HostSet hosts; public FallbackHostHandler(final HostResolver resolver) { this.resolver = resolver; lastFailExceptions = new ArrayList<>(); reset(); - hosts = resolver.resolve(true, false); + hosts = resolver.getHosts(); current = lastSuccess = hosts.getHostsList().get(0); - firstOpened = true; } @Override @@ -69,7 +67,7 @@ public void success() { @Override public void fail(Exception exception) { - hosts = resolver.resolve(false, false); + hosts = resolver.getHosts(); final List hostList = hosts.getHostsList(); final int index = hostList.indexOf(current) + 1; final boolean inBound = index < hostList.size(); @@ -93,30 +91,10 @@ public void reset() { lastFailExceptions.clear(); } - @Override - public void confirm() { - if (firstOpened) { - // after first successful established connection, update host list - hosts = resolver.resolve(false, false); - firstOpened = false; - } - } - @Override public void close() { hosts.close(); - } - - @Override - public void closeCurrentOnError() { - current.closeOnError(); - } - - @Override - public synchronized void closeCurrentOnErrorIfNotMatch(HostDescription host) { - if (!host.equals(current.getDescription())) { - closeCurrentOnError(); - } + resolver.close(); } @Override diff --git a/core/src/main/java/com/arangodb/internal/net/HostHandler.java b/core/src/main/java/com/arangodb/internal/net/HostHandler.java index 911a219b0..6337007d6 100644 --- a/core/src/main/java/com/arangodb/internal/net/HostHandler.java +++ b/core/src/main/java/com/arangodb/internal/net/HostHandler.java @@ -39,14 +39,8 @@ public interface HostHandler { void reset(); - void confirm(); - void close() throws IOException; - void closeCurrentOnError(); - - void closeCurrentOnErrorIfNotMatch(HostDescription host); - void setJwt(String jwt); } diff --git a/core/src/main/java/com/arangodb/internal/net/HostResolver.java b/core/src/main/java/com/arangodb/internal/net/HostResolver.java index 744ee9e87..d9bd4784a 100644 --- a/core/src/main/java/com/arangodb/internal/net/HostResolver.java +++ b/core/src/main/java/com/arangodb/internal/net/HostResolver.java @@ -28,8 +28,12 @@ */ public interface HostResolver { - void init(ArangoExecutorSync executorSync, InternalSerde arangoSerialization); + default void init(ArangoExecutorSync executorSync, InternalSerde arangoSerialization) { + } - HostSet resolve(boolean initial, boolean closeConnections); + default void close() { + } + + HostSet getHosts(); } diff --git a/core/src/main/java/com/arangodb/internal/net/RandomHostHandler.java b/core/src/main/java/com/arangodb/internal/net/RandomHostHandler.java index 4fd00e3b8..031ed380d 100644 --- a/core/src/main/java/com/arangodb/internal/net/RandomHostHandler.java +++ b/core/src/main/java/com/arangodb/internal/net/RandomHostHandler.java @@ -39,13 +39,15 @@ public RandomHostHandler(final HostResolver resolver, final HostHandler fallback super(); this.resolver = resolver; this.fallback = fallback; - current = getRandomHost(true, false); + hosts = resolver.getHosts(); + current = getRandomHost(); } @Override public Host get(final HostHandle hostHandle, AccessType accessType) { if (current == null) { - current = getRandomHost(false, true); + hosts = resolver.getHosts(); + current = getRandomHost(); } return current; } @@ -68,8 +70,7 @@ public synchronized void failIfNotMatch(HostDescription host, Exception exceptio } } - private Host getRandomHost(final boolean initial, final boolean closeConnections) { - hosts = resolver.resolve(initial, closeConnections); + private Host getRandomHost() { final ArrayList hostList = new ArrayList<>(hosts.getHostsList()); Collections.shuffle(hostList); return hostList.get(0); @@ -80,25 +81,10 @@ public void reset() { fallback.reset(); } - @Override - public void confirm() { - } - @Override public void close() { hosts.close(); - } - - @Override - public void closeCurrentOnError() { - current.closeOnError(); - } - - @Override - public synchronized void closeCurrentOnErrorIfNotMatch(HostDescription host) { - if (!host.equals(current.getDescription())) { - closeCurrentOnError(); - } + resolver.close(); } @Override diff --git a/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java b/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java index 23f5be99c..45a0069dd 100644 --- a/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java +++ b/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java @@ -43,14 +43,14 @@ public RoundRobinHostHandler(final HostResolver resolver) { super(); this.resolver = resolver; lastFailExceptions = new ArrayList<>(); - hosts = resolver.resolve(true, false); + hosts = resolver.getHosts(); current = 0L; reset(); } @Override public Host get(final HostHandle hostHandle, AccessType accessType) { - hosts = resolver.resolve(false, false); + hosts = resolver.getHosts(); final int size = hosts.getHostsList().size(); if (fails > size) { @@ -101,23 +101,10 @@ public void reset() { lastFailExceptions.clear(); } - @Override - public void confirm() { - } - @Override public void close() { hosts.close(); - } - - @Override - public void closeCurrentOnError() { - currentHost.closeOnError(); - } - - @Override - public void closeCurrentOnErrorIfNotMatch(HostDescription host) { - closeCurrentOnError(); + resolver.close(); } @Override diff --git a/core/src/main/java/com/arangodb/internal/net/SimpleHostResolver.java b/core/src/main/java/com/arangodb/internal/net/SimpleHostResolver.java index 1d1d8f8f5..f7d594f48 100644 --- a/core/src/main/java/com/arangodb/internal/net/SimpleHostResolver.java +++ b/core/src/main/java/com/arangodb/internal/net/SimpleHostResolver.java @@ -20,9 +20,6 @@ package com.arangodb.internal.net; -import com.arangodb.internal.ArangoExecutorSync; -import com.arangodb.internal.serde.InternalSerde; - import java.util.List; /** @@ -38,12 +35,7 @@ public SimpleHostResolver(final List hosts) { } @Override - public void init(ArangoExecutorSync executor, InternalSerde arangoSerialization) { - - } - - @Override - public HostSet resolve(final boolean initial, final boolean closeConnections) { + public HostSet getHosts() { return new HostSet(hosts); } diff --git a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java index 039a5f254..162f00e3b 100644 --- a/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDBAsyncTest.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import java.util.*; @@ -350,10 +351,13 @@ void updateUserDefaultCollectionAccess(ArangoDBAsync arangoDB) { arangoDB.grantDefaultCollectionAccess(username, Permissions.RW); } - @Test - void authenticationFailPassword() { + @ParameterizedTest + @EnumSource(Protocol.class) + void authenticationFailPassword(Protocol protocol) { final ArangoDBAsync arangoDB = new ArangoDB.Builder() .loadProperties(config) + .protocol(protocol) + .acquireHostList(false) .password("no").jwt(null) .build() .async(); @@ -362,11 +366,13 @@ void authenticationFailPassword() { assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(401); } - @ParameterizedTest(name = "{index}") - @MethodSource("asyncArangos") - void authenticationFailUser() { + @ParameterizedTest + @EnumSource(Protocol.class) + void authenticationFailUser(Protocol protocol) { final ArangoDBAsync arangoDB = new ArangoDB.Builder() .loadProperties(config) + .protocol(protocol) + .acquireHostList(false) .user("no").jwt(null) .build() .async(); diff --git a/driver/src/test/java/com/arangodb/ArangoDBTest.java b/driver/src/test/java/com/arangodb/ArangoDBTest.java index b02a351f4..c67271c7c 100644 --- a/driver/src/test/java/com/arangodb/ArangoDBTest.java +++ b/driver/src/test/java/com/arangodb/ArangoDBTest.java @@ -35,6 +35,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import java.util.*; @@ -352,21 +353,26 @@ void updateUserDefaultCollectionAccess(ArangoDB arangoDB) { arangoDB.grantDefaultCollectionAccess(username, Permissions.RW); } - @Test - void authenticationFailPassword() { + @ParameterizedTest + @EnumSource(Protocol.class) + void authenticationFailPassword(Protocol protocol) { final ArangoDB arangoDB = new ArangoDB.Builder() .loadProperties(config) + .protocol(protocol) + .acquireHostList(false) .password("no").jwt(null).build(); Throwable thrown = catchThrowable(arangoDB::getVersion); assertThat(thrown).isInstanceOf(ArangoDBException.class); assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(401); } - @ParameterizedTest(name = "{index}") - @MethodSource("arangos") - void authenticationFailUser() { + @ParameterizedTest + @EnumSource(Protocol.class) + void authenticationFailUser(Protocol protocol) { final ArangoDB arangoDB = new ArangoDB.Builder() .loadProperties(config) + .protocol(protocol) + .acquireHostList(false) .user("no").jwt(null).build(); Throwable thrown = catchThrowable(arangoDB::getVersion); assertThat(thrown).isInstanceOf(ArangoDBException.class); diff --git a/driver/src/test/java/com/arangodb/JwtAuthAsyncTest.java b/driver/src/test/java/com/arangodb/JwtAuthAsyncTest.java index cbeb68c9f..bff18088b 100644 --- a/driver/src/test/java/com/arangodb/JwtAuthAsyncTest.java +++ b/driver/src/test/java/com/arangodb/JwtAuthAsyncTest.java @@ -50,7 +50,7 @@ private static String getJwt(ArangoDB arangoDB) { @ParameterizedTest @EnumSource(Protocol.class) void notAuthenticated(Protocol protocol) { - ArangoDBAsync arangoDB = getBuilder(protocol).build().async(); + ArangoDBAsync arangoDB = getBuilder(protocol).acquireHostList(false).build().async(); Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); assertThat(thrown).isInstanceOf(ArangoDBException.class); ArangoDBException e = (ArangoDBException) thrown; diff --git a/driver/src/test/java/com/arangodb/JwtAuthTest.java b/driver/src/test/java/com/arangodb/JwtAuthTest.java index 2e4bedddc..137488d88 100644 --- a/driver/src/test/java/com/arangodb/JwtAuthTest.java +++ b/driver/src/test/java/com/arangodb/JwtAuthTest.java @@ -49,7 +49,7 @@ private static String getJwt(ArangoDB arangoDB) { @ParameterizedTest @EnumSource(Protocol.class) void notAuthenticated(Protocol protocol) { - ArangoDB arangoDB = getBuilder(protocol).build(); + ArangoDB arangoDB = getBuilder(protocol).acquireHostList(false).build(); Throwable thrown = catchThrowable(arangoDB::getVersion); assertThat(thrown).isInstanceOf(ArangoDBException.class); ArangoDBException e = (ArangoDBException) thrown; diff --git a/driver/src/test/java/com/arangodb/internal/HostHandlerTest.java b/driver/src/test/java/com/arangodb/internal/HostHandlerTest.java index 3803a3c4e..706d05e58 100644 --- a/driver/src/test/java/com/arangodb/internal/HostHandlerTest.java +++ b/driver/src/test/java/com/arangodb/internal/HostHandlerTest.java @@ -24,7 +24,6 @@ import com.arangodb.ArangoDBMultipleException; import com.arangodb.config.HostDescription; import com.arangodb.internal.net.*; -import com.arangodb.internal.serde.InternalSerde; import org.junit.jupiter.api.Test; import java.util.Collections; @@ -61,41 +60,19 @@ public void close() { }; private static final Host HOST_0 = new HostImpl(mockCP, new HostDescription("127.0.0.1", 8529)); - private static final HostResolver SINGLE_HOST = new HostResolver() { - - @Override - public HostSet resolve(final boolean initial, final boolean closeConnections) { - - HostSet set = new HostSet(Collections.emptyList()); - set.addHost(HOST_0); - return set; - } - - @Override - public void init(ArangoExecutorSync executor, InternalSerde arangoSerialization) { - - } - + private static final HostResolver SINGLE_HOST = () -> { + HostSet set = new HostSet(Collections.emptyList()); + set.addHost(HOST_0); + return set; }; private static final Host HOST_1 = new HostImpl(mockCP, new HostDescription("127.0.0.2", 8529)); private static final Host HOST_2 = new HostImpl(mockCP, new HostDescription("127.0.0.3", 8529)); - private static final HostResolver MULTIPLE_HOSTS = new HostResolver() { - - @Override - public HostSet resolve(final boolean initial, final boolean closeConnections) { - - HostSet set = new HostSet(Collections.emptyList()); - set.addHost(HOST_0); - set.addHost(HOST_1); - set.addHost(HOST_2); - return set; - } - - @Override - public void init(ArangoExecutorSync executor, InternalSerde arangoSerialization) { - - } - + private static final HostResolver MULTIPLE_HOSTS = () -> { + HostSet set = new HostSet(Collections.emptyList()); + set.addHost(HOST_0); + set.addHost(HOST_1); + set.addHost(HOST_2); + return set; }; @Test diff --git a/driver/src/test/resources/arangodb-with-prefix.properties b/driver/src/test/resources/arangodb-with-prefix.properties index 79ce26e82..36fb2d0a5 100644 --- a/driver/src/test/resources/arangodb-with-prefix.properties +++ b/driver/src/test/resources/arangodb-with-prefix.properties @@ -1,3 +1,3 @@ adb.hosts=172.28.0.1:8529 -adb.acquireHostList=false +adb.acquireHostList=true adb.password=test diff --git a/driver/src/test/resources/arangodb.properties b/driver/src/test/resources/arangodb.properties index 9941cd11a..fa580e439 100644 --- a/driver/src/test/resources/arangodb.properties +++ b/driver/src/test/resources/arangodb.properties @@ -1,5 +1,5 @@ -arangodb.hosts=172.28.0.1:8529,172.28.0.1:8539,172.28.0.1:8549 -arangodb.acquireHostList=false +arangodb.hosts=172.28.0.1:8529 +arangodb.acquireHostList=true arangodb.password=test arangodb.timeout=30000 arangodb.responseQueueTimeSamples=20 diff --git a/http/src/main/java/com/arangodb/http/HttpCommunication.java b/http/src/main/java/com/arangodb/http/HttpCommunication.java index c929d08c4..b2752cea9 100644 --- a/http/src/main/java/com/arangodb/http/HttpCommunication.java +++ b/http/src/main/java/com/arangodb/http/HttpCommunication.java @@ -117,7 +117,6 @@ private CompletableFuture executeAsync(final InternalRequest r rfuture.completeExceptionally(errorEntityEx); } else { hostHandler.success(); - hostHandler.confirm(); rfuture.complete(response); } } diff --git a/vst/src/main/java/com/arangodb/vst/VstCommunication.java b/vst/src/main/java/com/arangodb/vst/VstCommunication.java index 847da7107..03f3aaa53 100644 --- a/vst/src/main/java/com/arangodb/vst/VstCommunication.java +++ b/vst/src/main/java/com/arangodb/vst/VstCommunication.java @@ -92,7 +92,6 @@ protected synchronized C connect(final HostHandle hostHandle, final AccessType a if (jwt != null || user != null) { tryAuthenticate(connection); } - hostHandler.confirm(); if (!connection.isOpen()) { // see https://github.com/arangodb/arangodb-java-driver/issues/384 hostHandler.fail(new IOException("The connection is closed.")); diff --git a/vst/src/main/java/com/arangodb/vst/VstProtocol.java b/vst/src/main/java/com/arangodb/vst/VstProtocol.java index 750308b5d..bad1a5b0e 100644 --- a/vst/src/main/java/com/arangodb/vst/VstProtocol.java +++ b/vst/src/main/java/com/arangodb/vst/VstProtocol.java @@ -27,6 +27,8 @@ import java.io.IOException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * @author Mark Vollmary @@ -34,6 +36,7 @@ public class VstProtocol implements CommunicationProtocol { private final VstCommunicationAsync communication; + private final ExecutorService outgoingExecutor = Executors.newCachedThreadPool(); public VstProtocol(final VstCommunicationAsync communication) { super(); @@ -42,7 +45,8 @@ public VstProtocol(final VstCommunicationAsync communication) { @Override public CompletableFuture executeAsync(InternalRequest request, HostHandle hostHandle) { - return communication.execute(request, hostHandle); + return CompletableFuture.completedFuture(null) + .thenComposeAsync(__ -> communication.execute(request, hostHandle), outgoingExecutor); } @Override @@ -53,6 +57,7 @@ public void setJwt(String jwt) { @Override public void close() throws IOException { communication.close(); + outgoingExecutor.shutdown(); } } From 62672606e6a7f18a5b79f7db487af773b32a5284 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 24 Oct 2023 20:37:11 +0200 Subject: [PATCH 54/62] ArangoCursor refactoring --- .../java/com/arangodb/BaseArangoCursor.java | 4 +- .../com/arangodb/entity/CursorEntity.java | 13 +- .../internal/ArangoCursorExecute.java | 6 +- .../arangodb/internal/ArangoDatabaseImpl.java | 17 ++- .../internal/InternalArangoCursor.java | 4 +- .../internal/InternalArangoDatabase.java | 10 -- .../internal/cursor/ArangoCursorImpl.java | 43 +++--- .../cursor/entity/InternalCursorEntity.java | 127 ------------------ .../arangodb-java-driver/reflect-config.json | 2 +- .../reflect-config.json | 14 +- 10 files changed, 38 insertions(+), 202 deletions(-) delete mode 100644 core/src/main/java/com/arangodb/internal/cursor/entity/InternalCursorEntity.java diff --git a/core/src/main/java/com/arangodb/BaseArangoCursor.java b/core/src/main/java/com/arangodb/BaseArangoCursor.java index c2f48860e..6aeaaadd7 100644 --- a/core/src/main/java/com/arangodb/BaseArangoCursor.java +++ b/core/src/main/java/com/arangodb/BaseArangoCursor.java @@ -7,7 +7,7 @@ public interface BaseArangoCursor { String getId(); - Long getCount(); + Integer getCount(); Boolean isCached(); @@ -19,5 +19,5 @@ public interface BaseArangoCursor { String getNextBatchId(); - CursorEntity.Extra getExtra(); + CursorEntity.Extras getExtra(); } diff --git a/core/src/main/java/com/arangodb/entity/CursorEntity.java b/core/src/main/java/com/arangodb/entity/CursorEntity.java index b862563f2..3434a831d 100644 --- a/core/src/main/java/com/arangodb/entity/CursorEntity.java +++ b/core/src/main/java/com/arangodb/entity/CursorEntity.java @@ -33,16 +33,14 @@ */ public final class CursorEntity { private String id; - private Long count; + private Integer count; private Boolean cached; private Boolean hasMore; - - // TODO: test whether user-serde is used for result elements @UserDataInside private List result; private Boolean potentialDirtyRead; private String nextBatchId; - private final Extra extra = new Extra(); + private final Extras extra = new Extras(); public String getId() { return id; @@ -52,7 +50,7 @@ public String getId() { * @return the total number of result documents available (only available if the query was executed with the count * attribute set) */ - public Long getCount() { + public Integer getCount() { return count; } @@ -62,7 +60,7 @@ public Long getCount() { * documents and the number of documents that could not be modified due to an error (if ignoreErrors query * option is specified) */ - public Extra getExtra() { + public Extras getExtra() { return extra; } @@ -110,7 +108,7 @@ public String getNextBatchId() { return nextBatchId; } - public static final class Extra { + public static final class Extras { private final Collection warnings = Collections.emptyList(); private CursorStats stats; @@ -121,7 +119,6 @@ public CursorStats getStats() { public Collection getWarnings() { return warnings; } - } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoCursorExecute.java b/core/src/main/java/com/arangodb/internal/ArangoCursorExecute.java index 209f11cff..ebdd1fcd2 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCursorExecute.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCursorExecute.java @@ -20,15 +20,15 @@ package com.arangodb.internal; -import com.arangodb.internal.cursor.entity.InternalCursorEntity; +import com.arangodb.entity.CursorEntity; /** * @author Mark Vollmary */ -public interface ArangoCursorExecute { +public interface ArangoCursorExecute { - InternalCursorEntity next(String id, String nextBatchId); + CursorEntity next(String id, String nextBatchId); void close(String id); diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java index dd5b1ef27..f92147609 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java @@ -24,7 +24,6 @@ import com.arangodb.entity.*; import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; import com.arangodb.internal.cursor.ArangoCursorImpl; -import com.arangodb.internal.cursor.entity.InternalCursorEntity; import com.arangodb.internal.net.HostHandle; import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; @@ -168,7 +167,7 @@ public ArangoCursor query( final String query, final Class type, final Map bindVars, final AqlQueryOptions options) { final InternalRequest request = queryRequest(query, bindVars, options); final HostHandle hostHandle = new HostHandle(); - final InternalCursorEntity result = executorSync().execute(request, internalCursorEntityDeserializer(), hostHandle); + final CursorEntity result = executorSync().execute(request, cursorEntityDeserializer(type), hostHandle); return createCursor(result, type, options, hostHandle); } @@ -195,23 +194,23 @@ public ArangoCursor cursor(final String cursorId, final Class type) { @Override public ArangoCursor cursor(final String cursorId, final Class type, final String nextBatchId) { final HostHandle hostHandle = new HostHandle(); - final InternalCursorEntity result = executorSync().execute( + final CursorEntity result = executorSync().execute( queryNextRequest(cursorId, new AqlQueryOptions(), nextBatchId), - internalCursorEntityDeserializer(), + cursorEntityDeserializer(type), hostHandle); return createCursor(result, type, null, hostHandle); } private ArangoCursor createCursor( - final InternalCursorEntity result, + final CursorEntity result, final Class type, final AqlQueryOptions options, final HostHandle hostHandle) { - final ArangoCursorExecute execute = new ArangoCursorExecute() { + final ArangoCursorExecute execute = new ArangoCursorExecute() { @Override - public InternalCursorEntity next(final String id, final String nextBatchId) { - return executorSync().execute(queryNextRequest(id, options, nextBatchId), internalCursorEntityDeserializer(), hostHandle); + public CursorEntity next(final String id, final String nextBatchId) { + return executorSync().execute(queryNextRequest(id, options, nextBatchId), cursorEntityDeserializer(type), hostHandle); } @Override @@ -220,7 +219,7 @@ public void close(final String id) { } }; - return new ArangoCursorImpl<>(this, execute, type, result); + return new ArangoCursorImpl<>(execute, type, result); } @Override diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoCursor.java b/core/src/main/java/com/arangodb/internal/InternalArangoCursor.java index d0aea6634..2b6a63574 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoCursor.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoCursor.java @@ -58,7 +58,7 @@ public String getId() { } @Override - public Long getCount() { + public Integer getCount() { return entity.getCount(); } @@ -88,7 +88,7 @@ public String getNextBatchId() { } @Override - public CursorEntity.Extra getExtra() { + public CursorEntity.Extras getExtra() { return entity.getExtra(); } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java index 6d698f176..e9b7da8e0 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDatabase.java @@ -23,7 +23,6 @@ import com.arangodb.entity.*; import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; -import com.arangodb.internal.cursor.entity.InternalCursorEntity; import com.arangodb.internal.util.RequestUtils; import com.arangodb.model.*; import com.arangodb.model.arangosearch.*; @@ -232,15 +231,6 @@ protected InternalRequest deleteAqlFunctionRequest(final String name, final AqlF return request; } - protected ResponseDeserializer internalCursorEntityDeserializer() { - return response -> { - InternalCursorEntity e = getSerde().deserialize(response.getBody(), InternalCursorEntity.class); - boolean potentialDirtyRead = Boolean.parseBoolean(response.getMeta("X-Arango-Potential-Dirty-Read")); - e.setPontentialDirtyRead(potentialDirtyRead); - return e; - }; - } - public ResponseDeserializer> cursorEntityDeserializer(final Class type) { return response -> { CursorEntity e = getSerde().deserialize(response.getBody(), constructParametricType(CursorEntity.class, type)); diff --git a/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorImpl.java b/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorImpl.java index 0964d43f0..be5d24b28 100644 --- a/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorImpl.java +++ b/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorImpl.java @@ -22,12 +22,10 @@ import com.arangodb.ArangoCursor; import com.arangodb.ArangoIterator; +import com.arangodb.entity.CursorEntity; import com.arangodb.entity.CursorStats; import com.arangodb.entity.CursorWarning; import com.arangodb.internal.ArangoCursorExecute; -import com.arangodb.internal.InternalArangoDatabase; -import com.arangodb.internal.cursor.entity.InternalCursorEntity; -import com.fasterxml.jackson.databind.JsonNode; import java.util.Collection; import java.util.Iterator; @@ -41,18 +39,18 @@ public class ArangoCursorImpl implements ArangoCursor { protected final ArangoCursorIterator iterator; private final Class type; private final String id; - private final ArangoCursorExecute execute; + private final ArangoCursorExecute execute; private final boolean pontentialDirtyRead; private final boolean allowRetry; - public ArangoCursorImpl(final InternalArangoDatabase db, final ArangoCursorExecute execute, - final Class type, final InternalCursorEntity result) { + public ArangoCursorImpl(final ArangoCursorExecute execute, + final Class type, final CursorEntity result) { super(); this.execute = execute; this.type = type; id = result.getId(); - pontentialDirtyRead = result.isPontentialDirtyRead(); - iterator = new ArangoCursorIterator<>(id, type, execute, db, result); + pontentialDirtyRead = result.isPotentialDirtyRead(); + iterator = new ArangoCursorIterator<>(id, execute, result); this.allowRetry = result.getNextBatchId() != null; } @@ -85,13 +83,13 @@ public Integer getCount() { @Override public CursorStats getStats() { - final InternalCursorEntity.Extras extra = iterator.result.getExtra(); + final CursorEntity.Extras extra = iterator.result.getExtra(); return extra != null ? extra.getStats() : null; } @Override public Collection getWarnings() { - final InternalCursorEntity.Extras extra = iterator.result.getExtra(); + final CursorEntity.Extras extra = iterator.result.getExtra(); return extra != null ? extra.getWarnings() : null; } @@ -121,24 +119,20 @@ public String getNextBatchId() { return iterator.result.getNextBatchId(); } - protected ArangoCursorExecute getExecute() { + protected ArangoCursorExecute getExecute() { return execute; } protected static class ArangoCursorIterator implements ArangoIterator { private final String cursorId; - private final Class type; - private final InternalArangoDatabase db; - private final ArangoCursorExecute execute; - private InternalCursorEntity result; - private Iterator arrayIterator; - - protected ArangoCursorIterator(final String cursorId, final Class type, final ArangoCursorExecute execute, - final InternalArangoDatabase db, final InternalCursorEntity result) { + private final ArangoCursorExecute execute; + private CursorEntity result; + private Iterator arrayIterator; + + protected ArangoCursorIterator(final String cursorId, final ArangoCursorExecute execute, + final CursorEntity result) { this.cursorId = cursorId; - this.type = type; this.execute = execute; - this.db = db; this.result = result; arrayIterator = result.getResult().iterator(); } @@ -157,13 +151,8 @@ public T next() { if (!hasNext()) { throw new NoSuchElementException(); } - return deserialize(db.getSerde().serialize(arrayIterator.next()), type); - } - - private R deserialize(final byte[] result, final Class type) { - return db.getSerde().deserializeUserData(result, type); + return arrayIterator.next(); } - } } diff --git a/core/src/main/java/com/arangodb/internal/cursor/entity/InternalCursorEntity.java b/core/src/main/java/com/arangodb/internal/cursor/entity/InternalCursorEntity.java deleted file mode 100644 index 6d479d0fe..000000000 --- a/core/src/main/java/com/arangodb/internal/cursor/entity/InternalCursorEntity.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * DISCLAIMER - * - * Copyright 2016 ArangoDB GmbH, Cologne, Germany - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright holder is ArangoDB GmbH, Cologne, Germany - */ - -package com.arangodb.internal.cursor.entity; - -import com.arangodb.entity.CursorStats; -import com.arangodb.entity.CursorWarning; -import com.fasterxml.jackson.databind.JsonNode; - -import java.util.Collection; -import java.util.Collections; - -/** - * @author Mark Vollmary - * @see API - * Documentation - */ -public final class InternalCursorEntity { - - private final Extras extra = new Extras(); - private String id; - private Integer count; - private Boolean cached; - private Boolean hasMore; - private JsonNode result; - private Boolean pontentialDirtyRead; - private String nextBatchId; - - public String getId() { - return id; - } - - /** - * @return the total number of result documents available (only available if the query was executed with the count - * attribute set) - */ - public Integer getCount() { - return count; - } - - /** - * @return an optional object with extra information about the query result contained in its stats sub-attribute. - * For data-modification queries, the extra.stats sub-attribute will contain the number of modified - * documents and the number of documents that could not be modified due to an error (if ignoreErrors query - * option is specified) - */ - public Extras getExtra() { - return extra; - } - - /** - * @return a boolean flag indicating whether the query result was served from the query cache or not. If the query - * result is served from the query cache, the extra return attribute will not contain any stats - * sub-attribute and no profile sub-attribute. - */ - public Boolean getCached() { - return cached; - } - - /** - * @return A boolean indicator whether there are more results available for the cursor on the server - */ - public Boolean getHasMore() { - return hasMore; - } - - /** - * @return an array of result documents (might be empty if query has no results) - */ - public JsonNode getResult() { - return result; - } - - /** - * @return true if the result is a potential dirty read - * @since ArangoDB 3.10 - */ - public Boolean isPontentialDirtyRead() { - return pontentialDirtyRead; - } - - public void setPontentialDirtyRead(final Boolean pontentialDirtyRead) { - this.pontentialDirtyRead = pontentialDirtyRead; - } - - /** - * @return The ID of the batch after the current one. The first batch has an ID of 1 and the value is incremented by - * 1 with every batch. Only set if the allowRetry query option is enabled. - * @since ArangoDB 3.11 - */ - public String getNextBatchId() { - return nextBatchId; - } - - public static final class Extras { - private final Collection warnings = Collections.emptyList(); - private CursorStats stats; - - public CursorStats getStats() { - return stats; - } - - public Collection getWarnings() { - return warnings; - } - - } - -} - diff --git a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config.json b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config.json index b0ed104a7..a47e7957a 100644 --- a/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config.json +++ b/driver/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver/reflect-config.json @@ -1542,7 +1542,7 @@ "allDeclaredConstructors": true }, { - "name": "com.arangodb.entity.CursorEntity$Extra", + "name": "com.arangodb.entity.CursorEntity$Extras", "allDeclaredFields": true, "allDeclaredMethods": true, "allDeclaredConstructors": true diff --git a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config.json b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config.json index b0ed104a7..2553211b4 100644 --- a/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config.json +++ b/shaded/src/main/resources/META-INF/native-image/com.arangodb/arangodb-java-driver-shaded/reflect-config.json @@ -1523,18 +1523,6 @@ "allDeclaredMethods": true, "allDeclaredConstructors": true }, - { - "name": "com.arangodb.internal.cursor.entity.InternalCursorEntity", - "allDeclaredFields": true, - "allDeclaredMethods": true, - "allDeclaredConstructors": true - }, - { - "name": "com.arangodb.internal.cursor.entity.InternalCursorEntity$Extras", - "allDeclaredFields": true, - "allDeclaredMethods": true, - "allDeclaredConstructors": true - }, { "name": "com.arangodb.entity.CursorEntity", "allDeclaredFields": true, @@ -1542,7 +1530,7 @@ "allDeclaredConstructors": true }, { - "name": "com.arangodb.entity.CursorEntity$Extra", + "name": "com.arangodb.entity.CursorEntity$Extras", "allDeclaredFields": true, "allDeclaredMethods": true, "allDeclaredConstructors": true From f1ca3057b2ffef19c462061263d2b033cb9b8866 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 24 Oct 2023 21:00:53 +0200 Subject: [PATCH 55/62] fixed resilience tests --- vst/src/main/java/com/arangodb/vst/VstProtocol.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vst/src/main/java/com/arangodb/vst/VstProtocol.java b/vst/src/main/java/com/arangodb/vst/VstProtocol.java index bad1a5b0e..a125a853e 100644 --- a/vst/src/main/java/com/arangodb/vst/VstProtocol.java +++ b/vst/src/main/java/com/arangodb/vst/VstProtocol.java @@ -20,6 +20,7 @@ package com.arangodb.vst; +import com.arangodb.ArangoDBException; import com.arangodb.internal.InternalRequest; import com.arangodb.internal.InternalResponse; import com.arangodb.internal.net.CommunicationProtocol; @@ -45,6 +46,11 @@ public VstProtocol(final VstCommunicationAsync communication) { @Override public CompletableFuture executeAsync(InternalRequest request, HostHandle hostHandle) { + if (outgoingExecutor.isShutdown()) { + CompletableFuture cf = new CompletableFuture<>(); + cf.completeExceptionally(new ArangoDBException("VstProtocol already closed!")); + return cf; + } return CompletableFuture.completedFuture(null) .thenComposeAsync(__ -> communication.execute(request, hostHandle), outgoingExecutor); } From 224a443c1ef6388ba068ce0f4490dd7f4af7b55c Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 25 Oct 2023 10:29:39 +0200 Subject: [PATCH 56/62] fixed VST connection shutdown (DE-708) --- .../java/com/arangodb/internal/InternalArangoDB.java | 1 - .../arangodb/internal/net/RoundRobinHostHandler.java | 2 -- driver/src/test/java/CommunicationTest.java | 11 ++--------- vst/src/main/java/com/arangodb/vst/VstProtocol.java | 2 +- .../java/com/arangodb/vst/internal/MessageStore.java | 10 ---------- .../java/com/arangodb/vst/internal/VstConnection.java | 8 ++------ 6 files changed, 5 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java index a8159c1f7..9ddd14da5 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDB.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDB.java @@ -29,7 +29,6 @@ import com.arangodb.internal.ArangoExecutor.ResponseDeserializer; import com.arangodb.internal.config.ArangoConfig; import com.arangodb.internal.net.CommunicationProtocol; -import com.arangodb.internal.serde.InternalSerde; import com.arangodb.model.*; import java.util.ArrayList; diff --git a/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java b/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java index 45a0069dd..7d4861cdf 100644 --- a/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java +++ b/core/src/main/java/com/arangodb/internal/net/RoundRobinHostHandler.java @@ -36,7 +36,6 @@ public class RoundRobinHostHandler implements HostHandler { private final List lastFailExceptions; private long current; private int fails; - private Host currentHost; private HostSet hosts; public RoundRobinHostHandler(final HostResolver resolver) { @@ -75,7 +74,6 @@ public Host get(final HostHandle hostHandle, AccessType accessType) { hostHandle.setHost(host.getDescription()); } } - currentHost = host; return host; } diff --git a/driver/src/test/java/CommunicationTest.java b/driver/src/test/java/CommunicationTest.java index 7ab522187..c24ace7e6 100644 --- a/driver/src/test/java/CommunicationTest.java +++ b/driver/src/test/java/CommunicationTest.java @@ -10,7 +10,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import static org.junit.jupiter.api.Assumptions.assumeTrue; public class CommunicationTest { @@ -18,9 +17,6 @@ public class CommunicationTest { @EnumSource(Protocol.class) @Timeout(5) void disconnectAsync(Protocol protocol) throws InterruptedException, ExecutionException { - // FIXME: fix for VST protocol (DE-708) - assumeTrue(!Protocol.VST.equals(protocol)); - ArangoDBAsync arangoDB = new ArangoDB.Builder() .loadProperties(ArangoConfigProperties.fromFile()) .protocol(protocol) @@ -28,7 +24,7 @@ void disconnectAsync(Protocol protocol) throws InterruptedException, ExecutionEx .async(); arangoDB.getVersion().get(); - CompletableFuture> result = arangoDB.db().query("return sleep(1)", null, null, null); + CompletableFuture> result = arangoDB.db().query("return sleep(1)", null); Thread.sleep(500); arangoDB.shutdown(); Throwable thrown = catchThrowable(result::get).getCause(); @@ -44,9 +40,6 @@ void disconnectAsync(Protocol protocol) throws InterruptedException, ExecutionEx @EnumSource(Protocol.class) @Timeout(5) void disconnect(Protocol protocol) { - // FIXME: fix for VST protocol (DE-708) - assumeTrue(!Protocol.VST.equals(protocol)); - ArangoDB arangoDB = new ArangoDB.Builder() .loadProperties(ArangoConfigProperties.fromFile()) .protocol(protocol) @@ -62,7 +55,7 @@ void disconnect(Protocol protocol) { arangoDB.shutdown(); }).start(); - Throwable thrown = catchThrowable(() -> arangoDB.db().query("return sleep(1)", null, null, null)); + Throwable thrown = catchThrowable(() -> arangoDB.db().query("return sleep(1)", null)); assertThat(thrown) .isNotNull() .isInstanceOf(ArangoDBException.class); diff --git a/vst/src/main/java/com/arangodb/vst/VstProtocol.java b/vst/src/main/java/com/arangodb/vst/VstProtocol.java index a125a853e..c7acfb0ec 100644 --- a/vst/src/main/java/com/arangodb/vst/VstProtocol.java +++ b/vst/src/main/java/com/arangodb/vst/VstProtocol.java @@ -62,8 +62,8 @@ public void setJwt(String jwt) { @Override public void close() throws IOException { - communication.close(); outgoingExecutor.shutdown(); + communication.close(); } } diff --git a/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java b/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java index 707a3c325..1d8d6effb 100644 --- a/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java +++ b/vst/src/main/java/com/arangodb/vst/internal/MessageStore.java @@ -92,16 +92,6 @@ public synchronized void clear(final Exception e) { task.clear(); } - public synchronized void clear() { - for (final Entry> entry : task.entrySet()) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(String.format("Cancel Message (id=%s).", entry.getKey())); - } - entry.getValue().cancel(true); - } - task.clear(); - } - public boolean isEmpty() { return task.isEmpty(); } diff --git a/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java b/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java index 1cf653ae2..3b96c54eb 100644 --- a/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java +++ b/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java @@ -219,9 +219,9 @@ public synchronized void open() throws IOException { @Override public synchronized void close() { if (keepAliveScheduler != null) { - keepAliveScheduler.shutdownNow(); + keepAliveScheduler.shutdown(); } - messageStore.clear(); + messageStore.clear(new IOException("Connection closed")); if (executor != null && !executor.isShutdown()) { executor.shutdown(); } @@ -333,10 +333,6 @@ protected void readBytesIntoBuffer(final byte[] buf, final int off, final int le } } - public String getConnectionName() { - return this.connectionName; - } - @Override public void setJwt(String jwt) { // no-op: VST connections send jwt token only at initialization time From ec2a39efed0a89336006c4fa32722add1405d8c7 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 25 Oct 2023 11:23:26 +0200 Subject: [PATCH 57/62] fixed synchronous exceptions in HttpProtocol.executeAsync() --- http/src/main/java/com/arangodb/http/HttpProtocol.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http/src/main/java/com/arangodb/http/HttpProtocol.java b/http/src/main/java/com/arangodb/http/HttpProtocol.java index d3df5cd5b..67f8cdad3 100644 --- a/http/src/main/java/com/arangodb/http/HttpProtocol.java +++ b/http/src/main/java/com/arangodb/http/HttpProtocol.java @@ -42,7 +42,8 @@ public HttpProtocol(final HttpCommunication httpCommunication) { @Override public CompletableFuture executeAsync(final InternalRequest request, final HostHandle hostHandle) { - return httpCommunication.executeAsync(request, hostHandle); + return CompletableFuture.completedFuture(null) + .thenCompose(__ -> httpCommunication.executeAsync(request, hostHandle)); } @Override From cb7b702cb38870cb03d54c8d26a97344dba8b966 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 25 Oct 2023 11:31:48 +0200 Subject: [PATCH 58/62] async resilience tests --- .../resilience/connection/ConnectionTest.java | 50 ++++- .../test/java/resilience/http/MockTest.java | 24 +++ .../resilience/retry/RetriableCursorTest.java | 34 +++- .../test/java/resilience/retry/RetryTest.java | 177 +++++++++++++++++- .../resilience/shutdown/ShutdownTest.java | 36 ++++ .../java/resilience/timeout/TimeoutTest.java | 78 ++++++-- .../vstKeepAlive/VstKeepAliveCloseTest.java | 22 +++ 7 files changed, 383 insertions(+), 38 deletions(-) diff --git a/resilience-tests/src/test/java/resilience/connection/ConnectionTest.java b/resilience-tests/src/test/java/resilience/connection/ConnectionTest.java index eb17e409a..f41ff9b9d 100644 --- a/resilience-tests/src/test/java/resilience/connection/ConnectionTest.java +++ b/resilience-tests/src/test/java/resilience/connection/ConnectionTest.java @@ -1,9 +1,6 @@ package resilience.connection; -import com.arangodb.ArangoDB; -import com.arangodb.ArangoDBException; -import com.arangodb.ArangoDBMultipleException; -import com.arangodb.Protocol; +import com.arangodb.*; import resilience.SingleServerTest; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -36,9 +33,13 @@ static Stream arangoProvider() { ); } + static Stream asyncArangoProvider() { + return arangoProvider().map(ArangoDB::async); + } + @ParameterizedTest @MethodSource("protocolProvider") - void nameResolutionFailTest(Protocol protocol) { + void nameResolutionFail(Protocol protocol) { ArangoDB arangoDB = new ArangoDB.Builder() .host("wrongHost", 8529) .protocol(protocol) @@ -57,8 +58,29 @@ void nameResolutionFailTest(Protocol protocol) { } @ParameterizedTest + @MethodSource("protocolProvider") + void nameResolutionFailAsync(Protocol protocol) { + ArangoDBAsync arangoDB = new ArangoDB.Builder() + .host("wrongHost", 8529) + .protocol(protocol) + .build() + .async(); + + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(thrown.getMessage()).contains("Cannot contact any host!"); + assertThat(thrown.getCause()).isNotNull(); + assertThat(thrown.getCause()).isInstanceOf(ArangoDBMultipleException.class); + ((ArangoDBMultipleException) thrown.getCause()).getExceptions().forEach(e -> { + assertThat(e).isInstanceOf(UnknownHostException.class); + assertThat(e.getMessage()).contains("wrongHost"); + }); + arangoDB.shutdown(); + } + + @ParameterizedTest(name = "{index}") @MethodSource("arangoProvider") - void connectionFailTest(ArangoDB arangoDB) { + void connectionFail(ArangoDB arangoDB) { disableEndpoint(); Throwable thrown = catchThrowable(arangoDB::getVersion); @@ -72,4 +94,20 @@ void connectionFailTest(ArangoDB arangoDB) { enableEndpoint(); } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangoProvider") + void connectionFailAsync(ArangoDBAsync arangoDB) { + disableEndpoint(); + + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(thrown.getMessage()).contains("Cannot contact any host"); + assertThat(thrown.getCause()).isNotNull(); + assertThat(thrown.getCause()).isInstanceOf(ArangoDBMultipleException.class); + ((ArangoDBMultipleException) thrown.getCause()).getExceptions().forEach(e -> + assertThat(e).isInstanceOf(ConnectException.class)); + arangoDB.shutdown(); + enableEndpoint(); + } + } diff --git a/resilience-tests/src/test/java/resilience/http/MockTest.java b/resilience-tests/src/test/java/resilience/http/MockTest.java index 9c12a296f..5bba45018 100644 --- a/resilience-tests/src/test/java/resilience/http/MockTest.java +++ b/resilience-tests/src/test/java/resilience/http/MockTest.java @@ -9,6 +9,8 @@ import org.junit.jupiter.api.Test; import org.mockserver.integration.ClientAndServer; +import java.util.concurrent.ExecutionException; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockserver.integration.ClientAndServer.startClientAndServer; @@ -57,4 +59,26 @@ void doTest() { .isInstanceOf(ArangoDBException.class) .hasMessageContaining("boom"); } + + @Test + void doTestAsync() throws ExecutionException, InterruptedException { + arangoDB.async().getVersion().get(); + + mockServer + .when( + request() + .withMethod("GET") + .withPath("/.*/_api/version") + ) + .respond( + response() + .withStatusCode(503) + .withBody("{\"error\":true,\"errorNum\":503,\"errorMessage\":\"boom\",\"code\":503}") + ); + + Throwable thrown = catchThrowable(() -> arangoDB.async().getVersion().get()).getCause(); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .hasMessageContaining("boom"); + } } diff --git a/resilience-tests/src/test/java/resilience/retry/RetriableCursorTest.java b/resilience-tests/src/test/java/resilience/retry/RetriableCursorTest.java index 51f7c0c2f..aa38d498d 100644 --- a/resilience-tests/src/test/java/resilience/retry/RetriableCursorTest.java +++ b/resilience-tests/src/test/java/resilience/retry/RetriableCursorTest.java @@ -1,9 +1,6 @@ package resilience.retry; -import com.arangodb.ArangoCursor; -import com.arangodb.ArangoDB; -import com.arangodb.ArangoDBException; -import com.arangodb.Protocol; +import com.arangodb.*; import com.arangodb.model.AqlQueryOptions; import eu.rekawek.toxiproxy.model.ToxicDirection; import eu.rekawek.toxiproxy.model.toxic.Latency; @@ -12,6 +9,7 @@ import resilience.SingleServerTest; import java.io.IOException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.stream.Stream; @@ -31,7 +29,11 @@ static Stream arangoProvider() { ); } - @ParameterizedTest + static Stream asyncArangoProvider() { + return arangoProvider().map(ArangoDB::async); + } + + @ParameterizedTest(name = "{index}") @MethodSource("arangoProvider") void retryCursor(ArangoDB arangoDB) throws IOException { try (ArangoCursor cursor = arangoDB.db() @@ -53,4 +55,26 @@ void retryCursor(ArangoDB arangoDB) throws IOException { arangoDB.shutdown(); } + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangoProvider") + void retryCursorAsync(ArangoDBAsync arangoDB) throws IOException, ExecutionException, InterruptedException { + ArangoCursorAsync c1 = arangoDB.db() + .query("for i in 1..2 return i", + String.class, + new AqlQueryOptions().batchSize(1).allowRetry(true)).get(); + + assertThat(c1.getResult()).containsExactly("1"); + assertThat(c1.hasMore()).isTrue(); + Latency toxic = getEndpoint().getProxy().toxics().latency("latency", ToxicDirection.DOWNSTREAM, 10_000); + Throwable thrown = catchThrowable(() -> c1.nextBatch().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(thrown.getCause()).isInstanceOfAny(TimeoutException.class); + toxic.remove(); + ArangoCursorAsync c2 = c1.nextBatch().get(); + assertThat(c2.getResult()).containsExactly("2"); + assertThat(c2.hasMore()).isFalse(); + c2.close(); + arangoDB.shutdown(); + } + } diff --git a/resilience-tests/src/test/java/resilience/retry/RetryTest.java b/resilience-tests/src/test/java/resilience/retry/RetryTest.java index 27944f30d..6a7fc7d38 100644 --- a/resilience-tests/src/test/java/resilience/retry/RetryTest.java +++ b/resilience-tests/src/test/java/resilience/retry/RetryTest.java @@ -1,10 +1,7 @@ package resilience.retry; import ch.qos.logback.classic.Level; -import com.arangodb.ArangoDB; -import com.arangodb.ArangoDBException; -import com.arangodb.ArangoDBMultipleException; -import com.arangodb.Protocol; +import com.arangodb.*; import io.vertx.core.http.HttpClosedException; import org.junit.jupiter.params.provider.EnumSource; import resilience.SingleServerTest; @@ -16,10 +13,7 @@ import java.io.IOException; import java.net.ConnectException; import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.concurrent.*; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -39,13 +33,17 @@ static Stream arangoProvider() { ); } + static Stream asyncArangoProvider() { + return arangoProvider().map(ArangoDB::async); + } + /** * on reconnection failure: - 3x logs WARN Could not connect to host[addr=127.0.0.1,port=8529] - * ArangoDBException("Cannot contact any host") *

* once the proxy is re-enabled: - the subsequent requests should be successful */ - @ParameterizedTest + @ParameterizedTest(name = "{index}") @MethodSource("arangoProvider") void unreachableHost(ArangoDB arangoDB) { arangoDB.getVersion(); @@ -72,6 +70,39 @@ void unreachableHost(ArangoDB arangoDB) { arangoDB.shutdown(); } + /** + * on reconnection failure: - 3x logs WARN Could not connect to host[addr=127.0.0.1,port=8529] - + * ArangoDBException("Cannot contact any host") + *

+ * once the proxy is re-enabled: - the subsequent requests should be successful + */ + @ParameterizedTest(name = "{index}") + @MethodSource("asyncArangoProvider") + void unreachableHostAsync(ArangoDBAsync arangoDB) throws ExecutionException, InterruptedException { + arangoDB.getVersion().get(); + disableEndpoint(); + + for (int i = 0; i < 10; i++) { + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(thrown.getMessage()).contains("Cannot contact any host"); + assertThat(thrown.getCause()).isNotNull(); + assertThat(thrown.getCause()).isInstanceOf(ArangoDBMultipleException.class); + ((ArangoDBMultipleException) thrown.getCause()).getExceptions().forEach(e -> + assertThat(e).isInstanceOf(ConnectException.class)); + } + + long warnsCount = logs.getLoggedEvents().stream() + .filter(e -> e.getLevel().equals(Level.WARN)) + .filter(e -> e.getMessage().contains("Could not connect to host[addr=127.0.0.1,port=18529]")) + .count(); + assertThat(warnsCount).isGreaterThanOrEqualTo(3); + + enableEndpoint(); + arangoDB.getVersion().get(); + arangoDB.shutdown(); + } + /** * on delayed response: * - ArangoDBException with cause TimeoutException @@ -112,6 +143,47 @@ void connectionTimeout(Protocol protocol) throws IOException, InterruptedExcepti arangoDB.shutdown(); } + /** + * on delayed response: + * - ArangoDBException with cause TimeoutException + *

+ * once the delay is removed: + * - the subsequent requests should be successful + */ + @ParameterizedTest + @EnumSource(Protocol.class) + void connectionTimeoutAsync(Protocol protocol) throws IOException, InterruptedException, ExecutionException { + // https://github.com/vert-x3/vertx-web/issues/2296 + // WebClient: HTTP/2 request timeout does not throw TimeoutException + assumeTrue(protocol != Protocol.HTTP2_VPACK); + assumeTrue(protocol != Protocol.HTTP2_JSON); + + ArangoDBAsync arangoDB = dbBuilder() + .timeout(1_000) + .protocol(protocol) + .build() + .async(); + + arangoDB.getVersion().get(); + + // slow down the driver connection + Latency toxic = getEndpoint().getProxy().toxics().latency("latency", ToxicDirection.DOWNSTREAM, 10_000); + Thread.sleep(100); + + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + thrown.printStackTrace(); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .extracting(Throwable::getCause) + .isInstanceOf(TimeoutException.class); + + toxic.remove(); + Thread.sleep(100); + + arangoDB.getVersion().get(); + arangoDB.shutdown(); + } + /** * on closed pending requests of safe HTTP methods: @@ -160,6 +232,54 @@ void retryGetOnClosedConnection(Protocol protocol) throws IOException, Interrupt es.shutdown(); } + /** + * on closed pending requests of safe HTTP methods: + *

+ * - retry 3 times + * - ArangoDBMultipleException with 3 exceptions + *

+ * once restored: + *

+ * - the subsequent requests should be successful + */ + @ParameterizedTest + @EnumSource(Protocol.class) + void retryGetOnClosedConnectionAsync(Protocol protocol) throws IOException, InterruptedException, ExecutionException { + assumeTrue(protocol != Protocol.VST); + ArangoDBAsync arangoDB = dbBuilder() + .protocol(protocol) + .build() + .async(); + + arangoDB.getVersion().get(); + + // slow down the driver connection + Latency toxic = getEndpoint().getProxy().toxics().latency("latency", ToxicDirection.DOWNSTREAM, 10_000); + Thread.sleep(100); + + ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(); + es.schedule(this::disableEndpoint, 300, TimeUnit.MILLISECONDS); + + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + thrown.printStackTrace(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(thrown.getCause()).isInstanceOf(ArangoDBMultipleException.class); + List exceptions = ((ArangoDBMultipleException) thrown.getCause()).getExceptions(); + assertThat(exceptions).hasSize(3); + assertThat(exceptions.get(0)).isInstanceOf(IOException.class); + assertThat(exceptions.get(0).getCause()).isInstanceOf(HttpClosedException.class); + assertThat(exceptions.get(1)).isInstanceOf(ConnectException.class); + assertThat(exceptions.get(2)).isInstanceOf(ConnectException.class); + + toxic.remove(); + Thread.sleep(100); + enableEndpoint(); + + arangoDB.getVersion().get(); + arangoDB.shutdown(); + es.shutdown(); + } + /** * on closed pending requests of unsafe HTTP methods: - no retry should happen @@ -199,4 +319,43 @@ void notRetryPostOnClosedConnection(Protocol protocol) throws IOException, Inter es.shutdown(); } + /** + * on closed pending requests of unsafe HTTP methods: - no retry should happen + *

+ * once restored: - the subsequent requests should be successful + */ + @ParameterizedTest + @EnumSource(Protocol.class) + void notRetryPostOnClosedConnectionAsync(Protocol protocol) throws IOException, InterruptedException, ExecutionException { + ArangoDBAsync arangoDB = dbBuilder() + .protocol(protocol) + .build() + .async(); + + arangoDB.db().query("return null", Void.class).get(); + + // slow down the driver connection + Latency toxic = getEndpoint().getProxy().toxics().latency("latency", ToxicDirection.DOWNSTREAM, 10_000); + Thread.sleep(100); + + ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(); + es.schedule(this::disableEndpoint, 300, TimeUnit.MILLISECONDS); + + Throwable thrown = catchThrowable(() -> arangoDB.db().query("return null", Void.class).get()).getCause(); + thrown.printStackTrace(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(thrown.getCause()).isInstanceOf(IOException.class); + if (protocol != Protocol.VST) { + assertThat(thrown.getCause().getCause()).isInstanceOf(HttpClosedException.class); + } + + toxic.remove(); + Thread.sleep(100); + enableEndpoint(); + + arangoDB.db().query("return null", Void.class).get(); + arangoDB.shutdown(); + es.shutdown(); + } + } diff --git a/resilience-tests/src/test/java/resilience/shutdown/ShutdownTest.java b/resilience-tests/src/test/java/resilience/shutdown/ShutdownTest.java index 39cc1735f..855d6e4ca 100644 --- a/resilience-tests/src/test/java/resilience/shutdown/ShutdownTest.java +++ b/resilience-tests/src/test/java/resilience/shutdown/ShutdownTest.java @@ -1,6 +1,7 @@ package resilience.shutdown; import com.arangodb.ArangoDB; +import com.arangodb.ArangoDBAsync; import com.arangodb.ArangoDBException; import com.arangodb.Protocol; import io.vertx.core.http.HttpClosedException; @@ -9,6 +10,7 @@ import resilience.SingleServerTest; import java.io.IOException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -37,6 +39,22 @@ void shutdown(Protocol protocol) throws InterruptedException { assertThat(thrown.getMessage()).contains("closed"); } + @ParameterizedTest + @EnumSource(Protocol.class) + void shutdownAsync(Protocol protocol) throws InterruptedException, ExecutionException { + ArangoDBAsync arangoDB = dbBuilder() + .protocol(protocol) + .build() + .async(); + + arangoDB.getVersion().get(); + arangoDB.shutdown(); + Thread.sleep(1_000); + Throwable thrown = catchThrowable(() -> arangoDB.getVersion().get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(thrown.getMessage()).contains("closed"); + } + @ParameterizedTest @EnumSource(Protocol.class) void shutdownWithPendingRequests(Protocol protocol) { @@ -54,4 +72,22 @@ void shutdownWithPendingRequests(Protocol protocol) { es.shutdown(); } + @ParameterizedTest + @EnumSource(Protocol.class) + void shutdownWithPendingRequestsAsync(Protocol protocol) { + assumeTrue(protocol != Protocol.VST); + ArangoDBAsync arangoDB = dbBuilder() + .protocol(protocol) + .build() + .async(); + + ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(); + es.schedule(arangoDB::shutdown, 200, TimeUnit.MILLISECONDS); + Throwable thrown = catchThrowable(() -> arangoDB.db().query("return sleep(1)", Void.class).get()).getCause(); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(thrown.getCause()).isInstanceOf(IOException.class); + assertThat(thrown.getCause().getCause()).isInstanceOf(HttpClosedException.class); + es.shutdown(); + } + } diff --git a/resilience-tests/src/test/java/resilience/timeout/TimeoutTest.java b/resilience-tests/src/test/java/resilience/timeout/TimeoutTest.java index c9e3b1c88..a6c562eee 100644 --- a/resilience-tests/src/test/java/resilience/timeout/TimeoutTest.java +++ b/resilience-tests/src/test/java/resilience/timeout/TimeoutTest.java @@ -1,18 +1,17 @@ package resilience.timeout; -import com.arangodb.ArangoCollection; -import com.arangodb.ArangoDB; -import com.arangodb.ArangoDBException; -import com.arangodb.Protocol; +import com.arangodb.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import resilience.SingleServerTest; import java.util.Collections; import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.junit.jupiter.api.Assumptions.assumeTrue; /** @@ -47,20 +46,16 @@ void requestTimeout(Protocol protocol) throws InterruptedException { if (!col.exists()) col.create(); col.truncate(); - try { - arangoDB.db().query("" + - "INSERT {value:sleep(2)}\n" + - "INTO @@col\n" + - "RETURN NEW\n", - Map.class, - Collections.singletonMap("@col", colName)); - } catch (Exception e) { - e.printStackTrace(); - assertThat(e) - .isInstanceOf(ArangoDBException.class) - .extracting(Throwable::getCause) - .isInstanceOf(TimeoutException.class); - } + Throwable thrown = catchThrowable(() -> arangoDB.db() + .query("INSERT {value:sleep(2)} INTO @@col RETURN NEW", + Map.class, + Collections.singletonMap("@col", colName)) + ); + + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .extracting(Throwable::getCause) + .isInstanceOf(TimeoutException.class); arangoDB.getVersion(); @@ -70,4 +65,51 @@ void requestTimeout(Protocol protocol) throws InterruptedException { arangoDB.shutdown(); } + /** + * on timeout failure: + * - throw exception + * - expect operation performed (at most) once + *

+ * after the exception: + * - the subsequent requests should be successful + */ + @ParameterizedTest + @EnumSource(Protocol.class) + void requestTimeoutAsync(Protocol protocol) throws InterruptedException, ExecutionException { + // https://github.com/vert-x3/vertx-web/issues/2296 + // WebClient: HTTP/2 request timeout does not throw TimeoutException + assumeTrue(protocol != Protocol.HTTP2_VPACK); + assumeTrue(protocol != Protocol.HTTP2_JSON); + + ArangoDBAsync arangoDB = dbBuilder() + .timeout(1_000) + .protocol(protocol) + .build() + .async(); + + arangoDB.getVersion().get(); + String colName = "timeoutTest"; + ArangoCollectionAsync col = arangoDB.db().collection(colName); + if (!col.exists().get()) col.create().get(); + col.truncate().get(); + + Throwable thrown = catchThrowable(() -> arangoDB.db() + .query("INSERT {value:sleep(2)} INTO @@col RETURN NEW", + Map.class, + Collections.singletonMap("@col", colName)).get() + ).getCause(); + + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .extracting(Throwable::getCause) + .isInstanceOf(TimeoutException.class); + + arangoDB.getVersion().get(); + + Thread.sleep(2_000); + assertThat(col.count().get().getCount()).isEqualTo(1); + + arangoDB.shutdown(); + } + } diff --git a/resilience-tests/src/test/java/resilience/vstKeepAlive/VstKeepAliveCloseTest.java b/resilience-tests/src/test/java/resilience/vstKeepAlive/VstKeepAliveCloseTest.java index 2f0574651..7bb52285f 100644 --- a/resilience-tests/src/test/java/resilience/vstKeepAlive/VstKeepAliveCloseTest.java +++ b/resilience-tests/src/test/java/resilience/vstKeepAlive/VstKeepAliveCloseTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.api.Timeout; import java.io.IOException; +import java.util.concurrent.ExecutionException; import static org.awaitility.Awaitility.await; @@ -51,6 +52,27 @@ void keepAliveCloseAndReconnect() throws IOException { .filter(e -> e.getMessage() != null) .anyMatch(e -> e.getMessage().contains("Connection unresponsive!"))); toxic.setLatency(0); + toxic.remove(); arangoDB.getVersion(); } + + /** + * after 3 consecutive VST keepAlive failures: + * - log ERROR Connection unresponsive + * - reconnect on next request + */ + @Test + @Timeout(10) + void keepAliveCloseAndReconnectAsync() throws IOException, ExecutionException, InterruptedException { + arangoDB.async().getVersion().get(); + Latency toxic = getEndpoint().getProxy().toxics().latency("latency", ToxicDirection.DOWNSTREAM, 10_000); + await().until(() -> logs.getLoggedEvents().stream() + .filter(e -> e.getLevel().equals(Level.ERROR)) + .filter(e -> e.getMessage() != null) + .anyMatch(e -> e.getMessage().contains("Connection unresponsive!"))); + toxic.setLatency(0); + toxic.remove(); + arangoDB.async().getVersion().get(); + } + } From fe72c95ed2bd2fbbc25e262924851c08c823981c Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 25 Oct 2023 11:44:28 +0200 Subject: [PATCH 59/62] test fix --- .../src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java b/driver/src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java index d275849d3..b01c030e8 100644 --- a/driver/src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java +++ b/driver/src/test/java/com/arangodb/serde/CustomSerdeAsyncTest.java @@ -150,7 +150,7 @@ void aqlDeserialization() throws ExecutionException, InterruptedException { doc.put("arr", Collections.singletonList("hello")); doc.put("int", 10); - collection.insertDocument(doc); + collection.insertDocument(doc).get(); final Map result = db.query( "RETURN DOCUMENT(@docId)", From bf145a069d413c47decec46842afdbfae2092b34 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 25 Oct 2023 12:00:49 +0200 Subject: [PATCH 60/62] native tests fix --- .../META-INF/native-image/serialization-config.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/driver/src/test/resources/META-INF/native-image/serialization-config.json b/driver/src/test/resources/META-INF/native-image/serialization-config.json index 8c832f7ba..08c51a059 100644 --- a/driver/src/test/resources/META-INF/native-image/serialization-config.json +++ b/driver/src/test/resources/META-INF/native-image/serialization-config.json @@ -14,6 +14,12 @@ { "name": "java.lang.String" }, + { + "name":"java.lang.Integer" + }, + { + "name":"java.lang.Number" + }, { "name": "java.lang.Throwable" }, From c49ffb93e068670d63d4bb65de1938b3fa66587d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 25 Oct 2023 13:05:00 +0200 Subject: [PATCH 61/62] fix client side validations in async API (DE-703) --- .../internal/ArangoCollectionAsyncImpl.java | 102 +++++++++--------- .../internal/ArangoCollectionImpl.java | 2 - .../arangodb/internal/ArangoDBAsyncImpl.java | 38 +++---- .../internal/ArangoDatabaseAsyncImpl.java | 99 +++++++++-------- .../arangodb/internal/ArangoDatabaseImpl.java | 3 - .../ArangoEdgeCollectionAsyncImpl.java | 22 ++-- .../internal/ArangoExecutorAsync.java | 36 ++++--- .../internal/ArangoGraphAsyncImpl.java | 16 +-- .../internal/ArangoSearchAsyncImpl.java | 12 +-- .../ArangoVertexCollectionAsyncImpl.java | 22 ++-- .../internal/ArangoViewAsyncImpl.java | 6 +- .../internal/SearchAliasAsyncImpl.java | 12 +-- .../cursor/ArangoCursorAsyncImpl.java | 10 +- .../arangodb/ArangoCollectionAsyncTest.java | 2 +- .../com/arangodb/ArangoViewAsyncTest.java | 2 +- 15 files changed, 189 insertions(+), 195 deletions(-) diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java index a3a6de171..efe5bf700 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionAsyncImpl.java @@ -24,7 +24,6 @@ import com.arangodb.ArangoDBException; import com.arangodb.ArangoDatabaseAsync; import com.arangodb.entity.*; -import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; import com.arangodb.util.RawData; @@ -55,7 +54,7 @@ public ArangoDatabaseAsync db() { @Override public CompletableFuture> insertDocument(final Object value) { - return executorAsync().execute(insertDocumentRequest(value, new DocumentCreateOptions()), + return executorAsync().execute(() -> insertDocumentRequest(value, new DocumentCreateOptions()), constructParametricType(DocumentCreateEntity.class, Void.class)); } @@ -68,14 +67,14 @@ public CompletableFuture> insertDocument(final T val @Override public CompletableFuture> insertDocument(final T value, final DocumentCreateOptions options, final Class type) { - return executorAsync().execute(insertDocumentRequest(value, options), + return executorAsync().execute(() -> insertDocumentRequest(value, options), constructParametricType(DocumentCreateEntity.class, type)); } @Override public CompletableFuture>> insertDocuments(RawData values) { return executorAsync() - .execute(insertDocumentsRequest(values, new DocumentCreateOptions()), + .execute(() -> insertDocumentsRequest(values, new DocumentCreateOptions()), insertDocumentsResponseDeserializer(Void.class)); } @@ -84,7 +83,7 @@ public CompletableFuture>> insert public CompletableFuture>> insertDocuments(RawData values, DocumentCreateOptions options) { return executorAsync() - .execute(insertDocumentsRequest(values, options), + .execute(() -> insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer((Class) values.getClass())); } @@ -97,7 +96,7 @@ public CompletableFuture>> insert public CompletableFuture>> insertDocuments( final Iterable values, final DocumentCreateOptions options) { return executorAsync() - .execute(insertDocumentsRequest(values, options), + .execute(() -> insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer(Void.class)); } @@ -106,7 +105,7 @@ public CompletableFuture>> inser DocumentCreateOptions options, Class type) { return executorAsync() - .execute(insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer(type)); + .execute(() -> insertDocumentsRequest(values, options), insertDocumentsResponseDeserializer(type)); } @Override @@ -116,7 +115,7 @@ public CompletableFuture importDocuments(final Iterable @Override public CompletableFuture importDocuments(final Iterable values, final DocumentImportOptions options) { - return executorAsync().execute(importDocumentsRequest(values, options), DocumentImportEntity.class); + return executorAsync().execute(() -> importDocumentsRequest(values, options), DocumentImportEntity.class); } @Override @@ -126,7 +125,7 @@ public CompletableFuture importDocuments(RawData values) { @Override public CompletableFuture importDocuments(RawData values, DocumentImportOptions options) { - return executorAsync().execute(importDocumentsRequest(values, options), DocumentImportEntity.class); + return executorAsync().execute(() -> importDocumentsRequest(values, options), DocumentImportEntity.class); } @Override @@ -136,8 +135,7 @@ public CompletableFuture getDocument(final String key, final Class typ @Override public CompletableFuture getDocument(final String key, final Class type, final DocumentReadOptions options) { - DocumentUtil.validateDocumentKey(key); - return executorAsync().execute(getDocumentRequest(key, options), getDocumentResponseDeserializer(type)) + return executorAsync().execute(() -> getDocumentRequest(key, options), getDocumentResponseDeserializer(type)) .exceptionally(this::catchGetDocumentExceptions); } @@ -149,12 +147,12 @@ public CompletableFuture> getDocuments(final Iterable @Override public CompletableFuture> getDocuments( final Iterable keys, final Class type, final DocumentReadOptions options) { - return executorAsync().execute(getDocumentsRequest(keys, options), getDocumentsResponseDeserializer(type)); + return executorAsync().execute(() -> getDocumentsRequest(keys, options), getDocumentsResponseDeserializer(type)); } @Override public CompletableFuture> replaceDocument(final String key, final Object value) { - return executorAsync().execute(replaceDocumentRequest(key, value, new DocumentReplaceOptions()), + return executorAsync().execute(() -> replaceDocumentRequest(key, value, new DocumentReplaceOptions()), constructParametricType(DocumentUpdateEntity.class, Void.class)); } @@ -168,13 +166,13 @@ public CompletableFuture> replaceDocument( @Override public CompletableFuture> replaceDocument(String key, T value, DocumentReplaceOptions options, Class type) { - return executorAsync().execute(replaceDocumentRequest(key, value, options), + return executorAsync().execute(() -> replaceDocumentRequest(key, value, options), constructParametricType(DocumentUpdateEntity.class, type)); } @Override public CompletableFuture>> replaceDocuments(RawData values) { - return executorAsync().execute(replaceDocumentsRequest(values, new DocumentReplaceOptions()), + return executorAsync().execute(() -> replaceDocumentsRequest(values, new DocumentReplaceOptions()), replaceDocumentsResponseDeserializer(Void.class)); } @@ -182,7 +180,7 @@ public CompletableFuture>> replac @SuppressWarnings("unchecked") public CompletableFuture>> replaceDocuments(RawData values, DocumentReplaceOptions options) { - return executorAsync().execute(replaceDocumentsRequest(values, options), + return executorAsync().execute(() -> replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer((Class) values.getClass())); } @@ -194,7 +192,7 @@ public CompletableFuture>> replac @Override public CompletableFuture>> replaceDocuments( final Iterable values, final DocumentReplaceOptions options) { - return executorAsync().execute(replaceDocumentsRequest(values, options), + return executorAsync().execute(() -> replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(Void.class)); } @@ -202,7 +200,7 @@ public CompletableFuture>> replac public CompletableFuture>> replaceDocuments(Iterable values, DocumentReplaceOptions options, Class type) { - return executorAsync().execute(replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(type)); + return executorAsync().execute(() -> replaceDocumentsRequest(values, options), replaceDocumentsResponseDeserializer(type)); } @Override @@ -220,14 +218,14 @@ public CompletableFuture> updateDocument( @Override public CompletableFuture> updateDocument( final String key, final Object value, final DocumentUpdateOptions options, final Class returnType) { - return executorAsync().execute(updateDocumentRequest(key, value, options), + return executorAsync().execute(() -> updateDocumentRequest(key, value, options), constructParametricType(DocumentUpdateEntity.class, returnType)); } @Override public CompletableFuture>> updateDocuments(RawData values) { return executorAsync() - .execute(updateDocumentsRequest(values, new DocumentUpdateOptions()), + .execute(() -> updateDocumentsRequest(values, new DocumentUpdateOptions()), updateDocumentsResponseDeserializer(Void.class)); } @@ -236,7 +234,7 @@ public CompletableFuture>> update public CompletableFuture>> updateDocuments(RawData values, DocumentUpdateOptions options) { return executorAsync() - .execute(updateDocumentsRequest(values, options), + .execute(() -> updateDocumentsRequest(values, options), updateDocumentsResponseDeserializer((Class) values.getClass())); } @@ -255,7 +253,7 @@ public CompletableFuture>> update public CompletableFuture>> updateDocuments( final Iterable values, final DocumentUpdateOptions options, final Class returnType) { return executorAsync() - .execute(updateDocumentsRequest(values, options), updateDocumentsResponseDeserializer(returnType)); + .execute(() -> updateDocumentsRequest(values, options), updateDocumentsResponseDeserializer(returnType)); } @Override @@ -271,13 +269,13 @@ public CompletableFuture> deleteDocument(String key, @Override public CompletableFuture> deleteDocument( final String key, final DocumentDeleteOptions options, final Class type) { - return executorAsync().execute(deleteDocumentRequest(key, options), + return executorAsync().execute(() -> deleteDocumentRequest(key, options), constructParametricType(DocumentDeleteEntity.class, type)); } @Override public CompletableFuture>> deleteDocuments(RawData values) { - return executorAsync().execute(deleteDocumentsRequest(values, new DocumentDeleteOptions()), + return executorAsync().execute(() -> deleteDocumentsRequest(values, new DocumentDeleteOptions()), deleteDocumentsResponseDeserializer(Void.class)); } @@ -285,7 +283,7 @@ public CompletableFuture>> delete @SuppressWarnings("unchecked") public CompletableFuture>> deleteDocuments(RawData values, DocumentDeleteOptions options) { - return executorAsync().execute(deleteDocumentsRequest(values, options), + return executorAsync().execute(() -> deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer((Class) values.getClass())); } @@ -303,7 +301,7 @@ public CompletableFuture>> delete @Override public CompletableFuture>> deleteDocuments( final Iterable values, final DocumentDeleteOptions options, final Class type) { - return executorAsync().execute(deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer(type)); + return executorAsync().execute(() -> deleteDocumentsRequest(values, options), deleteDocumentsResponseDeserializer(type)); } @Override @@ -313,7 +311,7 @@ public CompletableFuture documentExists(final String key) { @Override public CompletableFuture documentExists(final String key, final DocumentExistsOptions options) { - return executorAsync().execute(documentExistsRequest(key, options), Void.class) + return executorAsync().execute(() -> documentExistsRequest(key, options), Void.class) .thenApply(it -> true) .exceptionally(this::catchGetDocumentExceptions) .thenApply(Objects::nonNull); @@ -339,58 +337,58 @@ T catchGetDocumentExceptions(Throwable err) { @Override public CompletableFuture getIndex(final String id) { - return executorAsync().execute(getIndexRequest(id), IndexEntity.class); + return executorAsync().execute(() -> getIndexRequest(id), IndexEntity.class); } @Override public CompletableFuture getInvertedIndex(String id) { - return executorAsync().execute(getIndexRequest(id), InvertedIndexEntity.class); + return executorAsync().execute(() -> getIndexRequest(id), InvertedIndexEntity.class); } @Override public CompletableFuture deleteIndex(final String id) { - return executorAsync().execute(deleteIndexRequest(id), deleteIndexResponseDeserializer()); + return executorAsync().execute(() -> deleteIndexRequest(id), deleteIndexResponseDeserializer()); } @Override public CompletableFuture ensurePersistentIndex(final Iterable fields, final PersistentIndexOptions options) { - return executorAsync().execute(createPersistentIndexRequest(fields, options), IndexEntity.class); + return executorAsync().execute(() -> createPersistentIndexRequest(fields, options), IndexEntity.class); } @Override public CompletableFuture ensureInvertedIndex(final InvertedIndexOptions options) { - return executorAsync().execute(createInvertedIndexRequest(options), InvertedIndexEntity.class); + return executorAsync().execute(() -> createInvertedIndexRequest(options), InvertedIndexEntity.class); } @Override public CompletableFuture ensureGeoIndex(final Iterable fields, final GeoIndexOptions options) { - return executorAsync().execute(createGeoIndexRequest(fields, options), IndexEntity.class); + return executorAsync().execute(() -> createGeoIndexRequest(fields, options), IndexEntity.class); } @Deprecated @Override public CompletableFuture ensureFulltextIndex(final Iterable fields, final FulltextIndexOptions options) { - return executorAsync().execute(createFulltextIndexRequest(fields, options), IndexEntity.class); + return executorAsync().execute(() -> createFulltextIndexRequest(fields, options), IndexEntity.class); } @Override public CompletableFuture ensureTtlIndex(final Iterable fields, final TtlIndexOptions options) { - return executorAsync().execute(createTtlIndexRequest(fields, options), IndexEntity.class); + return executorAsync().execute(() -> createTtlIndexRequest(fields, options), IndexEntity.class); } @Override public CompletableFuture ensureZKDIndex(final Iterable fields, final ZKDIndexOptions options) { - return executorAsync().execute(createZKDIndexRequest(fields, options), IndexEntity.class); + return executorAsync().execute(() -> createZKDIndexRequest(fields, options), IndexEntity.class); } @Override public CompletableFuture> getIndexes() { - return executorAsync().execute(getIndexesRequest(), getIndexesResponseDeserializer()); + return executorAsync().execute(this::getIndexesRequest, getIndexesResponseDeserializer()); } @Override public CompletableFuture> getInvertedIndexes() { - return executorAsync().execute(getIndexesRequest(), getInvertedIndexesResponseDeserializer()); + return executorAsync().execute(this::getIndexesRequest, getInvertedIndexesResponseDeserializer()); } @Override @@ -416,7 +414,7 @@ public CompletableFuture truncate() { @Override public CompletableFuture truncate(CollectionTruncateOptions options) { - return executorAsync().execute(truncateRequest(options), CollectionEntity.class); + return executorAsync().execute(() -> truncateRequest(options), CollectionEntity.class); } @Override @@ -426,7 +424,7 @@ public CompletableFuture count() { @Override public CompletableFuture count(CollectionCountOptions options) { - return executorAsync().execute(countRequest(options), CollectionPropertiesEntity.class); + return executorAsync().execute(() -> countRequest(options), CollectionPropertiesEntity.class); } @Override @@ -441,62 +439,62 @@ public CompletableFuture create(final CollectionCreateOptions @Override public CompletableFuture drop() { - return executorAsync().execute(dropRequest(null), Void.class); + return executorAsync().execute(() -> dropRequest(null), Void.class); } @Override public CompletableFuture drop(final boolean isSystem) { - return executorAsync().execute(dropRequest(isSystem), Void.class); + return executorAsync().execute(() -> dropRequest(isSystem), Void.class); } @Override public CompletableFuture getInfo() { - return executorAsync().execute(getInfoRequest(), CollectionEntity.class); + return executorAsync().execute(this::getInfoRequest, CollectionEntity.class); } @Override public CompletableFuture getProperties() { - return executorAsync().execute(getPropertiesRequest(), CollectionPropertiesEntity.class); + return executorAsync().execute(this::getPropertiesRequest, CollectionPropertiesEntity.class); } @Override public CompletableFuture changeProperties(final CollectionPropertiesOptions options) { - return executorAsync().execute(changePropertiesRequest(options), CollectionPropertiesEntity.class); + return executorAsync().execute(() -> changePropertiesRequest(options), CollectionPropertiesEntity.class); } @Override public CompletableFuture rename(final String newName) { - return executorAsync().execute(renameRequest(newName), CollectionEntity.class); + return executorAsync().execute(() -> renameRequest(newName), CollectionEntity.class); } @Override public CompletableFuture getResponsibleShard(final Object value) { - return executorAsync().execute(responsibleShardRequest(value), ShardEntity.class); + return executorAsync().execute(() -> responsibleShardRequest(value), ShardEntity.class); } @Override public CompletableFuture getRevision() { - return executorAsync().execute(getRevisionRequest(), CollectionRevisionEntity.class); + return executorAsync().execute(this::getRevisionRequest, CollectionRevisionEntity.class); } @Override public CompletableFuture grantAccess(final String user, final Permissions permissions) { - return executorAsync().execute(grantAccessRequest(user, permissions), Void.class); + return executorAsync().execute(() -> grantAccessRequest(user, permissions), Void.class); } @Override public CompletableFuture revokeAccess(final String user) { - return executorAsync().execute(grantAccessRequest(user, Permissions.NONE), Void.class); + return executorAsync().execute(() -> grantAccessRequest(user, Permissions.NONE), Void.class); } @Override public CompletableFuture resetAccess(final String user) { - return executorAsync().execute(resetAccessRequest(user), Void.class); + return executorAsync().execute(() -> resetAccessRequest(user), Void.class); } @Override public CompletableFuture getPermissions(final String user) { - return executorAsync().execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); + return executorAsync().execute(() -> getPermissionsRequest(user), getPermissionsResponseDeserialzer()); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java b/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java index 7f3422efd..e739e8fa2 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoCollectionImpl.java @@ -24,7 +24,6 @@ import com.arangodb.ArangoDBException; import com.arangodb.ArangoDatabase; import com.arangodb.entity.*; -import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; import com.arangodb.util.RawData; import org.slf4j.Logger; @@ -137,7 +136,6 @@ public T getDocument(final String key, final Class type) { @Override public T getDocument(final String key, final Class type, final DocumentReadOptions options) { - DocumentUtil.validateDocumentKey(key); try { return executorSync().execute(getDocumentRequest(key, options), getDocumentResponseDeserializer(type)); } catch (final ArangoDBException e) { diff --git a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java index 173a211fb..76593b8c6 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDBAsyncImpl.java @@ -74,12 +74,12 @@ public CompletableFuture createDatabase(final String dbName) { @Override public CompletableFuture createDatabase(DBCreateOptions options) { - return executorAsync().execute(createDatabaseRequest(options), createDatabaseResponseDeserializer()); + return executorAsync().execute(() -> createDatabaseRequest(options), createDatabaseResponseDeserializer()); } @Override public CompletableFuture> getDatabases() { - return executorAsync().execute(getDatabasesRequest(ArangoRequestParam.SYSTEM), getDatabaseResponseDeserializer()); + return executorAsync().execute(() -> getDatabasesRequest(ArangoRequestParam.SYSTEM), getDatabaseResponseDeserializer()); } @Override @@ -89,7 +89,7 @@ public CompletableFuture> getAccessibleDatabases() { @Override public CompletableFuture> getAccessibleDatabasesFor(final String user) { - return executorAsync().execute(getAccessibleDatabasesForRequest(ArangoRequestParam.SYSTEM, user), + return executorAsync().execute(() -> getAccessibleDatabasesForRequest(ArangoRequestParam.SYSTEM, user), getAccessibleDatabasesForResponseDeserializer()); } @@ -105,68 +105,68 @@ public CompletableFuture getEngine() { @Override public CompletableFuture getRole() { - return executorAsync().execute(getRoleRequest(), getRoleResponseDeserializer()); + return executorAsync().execute(this::getRoleRequest, getRoleResponseDeserializer()); } @Override public CompletableFuture getServerId() { - return executorAsync().execute(getServerIdRequest(), getServerIdResponseDeserializer()); + return executorAsync().execute(this::getServerIdRequest, getServerIdResponseDeserializer()); } @Override public CompletableFuture createUser(final String user, final String passwd) { - return executorAsync().execute(createUserRequest(ArangoRequestParam.SYSTEM, user, passwd, new UserCreateOptions()), + return executorAsync().execute(() -> createUserRequest(ArangoRequestParam.SYSTEM, user, passwd, new UserCreateOptions()), UserEntity.class); } @Override public CompletableFuture createUser(final String user, final String passwd, final UserCreateOptions options) { - return executorAsync().execute(createUserRequest(ArangoRequestParam.SYSTEM, user, passwd, options), UserEntity.class); + return executorAsync().execute(() -> createUserRequest(ArangoRequestParam.SYSTEM, user, passwd, options), UserEntity.class); } @Override public CompletableFuture deleteUser(final String user) { - return executorAsync().execute(deleteUserRequest(ArangoRequestParam.SYSTEM, user), Void.class); + return executorAsync().execute(() -> deleteUserRequest(ArangoRequestParam.SYSTEM, user), Void.class); } @Override public CompletableFuture getUser(final String user) { - return executorAsync().execute(getUserRequest(ArangoRequestParam.SYSTEM, user), UserEntity.class); + return executorAsync().execute(() -> getUserRequest(ArangoRequestParam.SYSTEM, user), UserEntity.class); } @Override public CompletableFuture> getUsers() { - return executorAsync().execute(getUsersRequest(ArangoRequestParam.SYSTEM), getUsersResponseDeserializer()); + return executorAsync().execute(() -> getUsersRequest(ArangoRequestParam.SYSTEM), getUsersResponseDeserializer()); } @Override public CompletableFuture updateUser(final String user, final UserUpdateOptions options) { - return executorAsync().execute(updateUserRequest(ArangoRequestParam.SYSTEM, user, options), UserEntity.class); + return executorAsync().execute(() -> updateUserRequest(ArangoRequestParam.SYSTEM, user, options), UserEntity.class); } @Override public CompletableFuture replaceUser(final String user, final UserUpdateOptions options) { - return executorAsync().execute(replaceUserRequest(ArangoRequestParam.SYSTEM, user, options), UserEntity.class); + return executorAsync().execute(() -> replaceUserRequest(ArangoRequestParam.SYSTEM, user, options), UserEntity.class); } @Override public CompletableFuture grantDefaultDatabaseAccess(final String user, final Permissions permissions) { - return executorAsync().execute(updateUserDefaultDatabaseAccessRequest(user, permissions), Void.class); + return executorAsync().execute(() -> updateUserDefaultDatabaseAccessRequest(user, permissions), Void.class); } @Override public CompletableFuture grantDefaultCollectionAccess(final String user, final Permissions permissions) { - return executorAsync().execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); + return executorAsync().execute(() -> updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); } @Override public CompletableFuture> execute(Request request, Class type) { - return executorAsync().execute(executeRequest(request), responseDeserializer(type)); + return executorAsync().execute(() -> executeRequest(request), responseDeserializer(type)); } @Override public CompletableFuture getLogEntries(final LogOptions options) { - return executorAsync().execute(getLogEntriesRequest(options), LogEntriesEntity.class); + return executorAsync().execute(() -> getLogEntriesRequest(options), LogEntriesEntity.class); } @Override @@ -176,7 +176,7 @@ public CompletableFuture getLogLevel() { @Override public CompletableFuture getLogLevel(final LogLevelOptions options) { - return executorAsync().execute(getLogLevelRequest(options), LogLevelEntity.class); + return executorAsync().execute(() -> getLogLevelRequest(options), LogLevelEntity.class); } @Override @@ -186,12 +186,12 @@ public CompletableFuture setLogLevel(final LogLevelEntity entity @Override public CompletableFuture setLogLevel(final LogLevelEntity entity, final LogLevelOptions options) { - return executorAsync().execute(setLogLevelRequest(entity, options), LogLevelEntity.class); + return executorAsync().execute(() -> setLogLevelRequest(entity, options), LogLevelEntity.class); } @Override public CompletableFuture> getQueryOptimizerRules() { - return executorAsync().execute(getQueryOptimizerRulesRequest(), SerdeUtils.constructListType(QueryOptimizerRule.class)); + return executorAsync().execute(this::getQueryOptimizerRulesRequest, SerdeUtils.constructListType(QueryOptimizerRule.class)); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java index eb3cac9d1..f3d46f7cc 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseAsyncImpl.java @@ -25,7 +25,6 @@ import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; import com.arangodb.internal.cursor.ArangoCursorAsyncImpl; import com.arangodb.internal.net.HostHandle; -import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; import com.arangodb.model.arangosearch.AnalyzerDeleteOptions; import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; @@ -58,12 +57,12 @@ public ArangoDBAsync arango() { @Override public CompletableFuture getVersion() { - return executorAsync().execute(getVersionRequest(), ArangoDBVersion.class); + return executorAsync().execute(this::getVersionRequest, ArangoDBVersion.class); } @Override public CompletableFuture getEngine() { - return executorAsync().execute(getEngineRequest(), ArangoDBEngine.class); + return executorAsync().execute(this::getEngineRequest, ArangoDBEngine.class); } @Override @@ -87,7 +86,7 @@ public CompletableFuture exists() { @Override public CompletableFuture> getAccessibleDatabases() { - return executorAsync().execute(getAccessibleDatabasesRequest(), getDatabaseResponseDeserializer()); + return executorAsync().execute(this::getAccessibleDatabasesRequest, getDatabaseResponseDeserializer()); } @Override @@ -97,35 +96,33 @@ public ArangoCollectionAsync collection(String name) { @Override public CompletableFuture createCollection(final String name) { - return executorAsync().execute(createCollectionRequest(name, new CollectionCreateOptions()), CollectionEntity.class); + return executorAsync().execute(() -> createCollectionRequest(name, new CollectionCreateOptions()), CollectionEntity.class); } @Override public CompletableFuture createCollection(final String name, final CollectionCreateOptions options) { - return executorAsync().execute(createCollectionRequest(name, options), CollectionEntity.class); + return executorAsync().execute(() -> createCollectionRequest(name, options), CollectionEntity.class); } @Override public CompletableFuture> getCollections() { return executorAsync() - .execute(getCollectionsRequest(new CollectionsReadOptions()), getCollectionsResponseDeserializer()); + .execute(() -> getCollectionsRequest(new CollectionsReadOptions()), getCollectionsResponseDeserializer()); } @Override public CompletableFuture> getCollections(final CollectionsReadOptions options) { - return executorAsync().execute(getCollectionsRequest(options), getCollectionsResponseDeserializer()); + return executorAsync().execute(() -> getCollectionsRequest(options), getCollectionsResponseDeserializer()); } @Override public CompletableFuture getIndex(final String id) { - DocumentUtil.validateIndexId(id); final String[] split = id.split("/"); return collection(split[0]).getIndex(split[1]); } @Override public CompletableFuture deleteIndex(final String id) { - DocumentUtil.validateIndexId(id); final String[] split = id.split("/"); return collection(split[0]).deleteIndex(split[1]); } @@ -137,37 +134,37 @@ public CompletableFuture create() { @Override public CompletableFuture drop() { - return executorAsync().execute(dropRequest(), createDropResponseDeserializer()); + return executorAsync().execute(this::dropRequest, createDropResponseDeserializer()); } @Override public CompletableFuture grantAccess(final String user, final Permissions permissions) { - return executorAsync().execute(grantAccessRequest(user, permissions), Void.class); + return executorAsync().execute(() -> grantAccessRequest(user, permissions), Void.class); } @Override public CompletableFuture grantAccess(final String user) { - return executorAsync().execute(grantAccessRequest(user, Permissions.RW), Void.class); + return executorAsync().execute(() -> grantAccessRequest(user, Permissions.RW), Void.class); } @Override public CompletableFuture revokeAccess(final String user) { - return executorAsync().execute(grantAccessRequest(user, Permissions.NONE), Void.class); + return executorAsync().execute(() -> grantAccessRequest(user, Permissions.NONE), Void.class); } @Override public CompletableFuture resetAccess(final String user) { - return executorAsync().execute(resetAccessRequest(user), Void.class); + return executorAsync().execute(() -> resetAccessRequest(user), Void.class); } @Override public CompletableFuture grantDefaultCollectionAccess(final String user, final Permissions permissions) { - return executorAsync().execute(updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); + return executorAsync().execute(() -> updateUserDefaultCollectionAccessRequest(user, permissions), Void.class); } @Override public CompletableFuture getPermissions(final String user) { - return executorAsync().execute(getPermissionsRequest(user), getPermissionsResponseDeserialzer()); + return executorAsync().execute(() -> getPermissionsRequest(user), getPermissionsResponseDeserialzer()); } @Override @@ -175,7 +172,7 @@ public CompletableFuture> query( final String query, final Class type, final Map bindVars, final AqlQueryOptions options) { final InternalRequest request = queryRequest(query, bindVars, options); final HostHandle hostHandle = new HostHandle(); - return executorAsync().execute(request, cursorEntityDeserializer(type), hostHandle) + return executorAsync().execute(() -> request, cursorEntityDeserializer(type), hostHandle) .thenApply(res -> new ArangoCursorAsyncImpl<>(this, res, type, hostHandle, options.getAllowRetry())); } @@ -203,7 +200,7 @@ public CompletableFuture> cursor(final String cursorId, public CompletableFuture> cursor(final String cursorId, final Class type, final String nextBatchId) { final HostHandle hostHandle = new HostHandle(); return executorAsync() - .execute( + .execute(() -> queryNextRequest(cursorId, new AqlQueryOptions(), nextBatchId), cursorEntityDeserializer(type), hostHandle) @@ -213,75 +210,75 @@ public CompletableFuture> cursor(final String cursorId, @Override public CompletableFuture explainQuery( final String query, final Map bindVars, final AqlQueryExplainOptions options) { - return executorAsync().execute(explainQueryRequest(query, bindVars, options), AqlExecutionExplainEntity.class); + return executorAsync().execute(() -> explainQueryRequest(query, bindVars, options), AqlExecutionExplainEntity.class); } @Override public CompletableFuture parseQuery(final String query) { - return executorAsync().execute(parseQueryRequest(query), AqlParseEntity.class); + return executorAsync().execute(() -> parseQueryRequest(query), AqlParseEntity.class); } @Override public CompletableFuture clearQueryCache() { - return executorAsync().execute(clearQueryCacheRequest(), Void.class); + return executorAsync().execute(this::clearQueryCacheRequest, Void.class); } @Override public CompletableFuture getQueryCacheProperties() { - return executorAsync().execute(getQueryCachePropertiesRequest(), QueryCachePropertiesEntity.class); + return executorAsync().execute(this::getQueryCachePropertiesRequest, QueryCachePropertiesEntity.class); } @Override public CompletableFuture setQueryCacheProperties(final QueryCachePropertiesEntity properties) { - return executorAsync().execute(setQueryCachePropertiesRequest(properties), QueryCachePropertiesEntity.class); + return executorAsync().execute(() -> setQueryCachePropertiesRequest(properties), QueryCachePropertiesEntity.class); } @Override public CompletableFuture getQueryTrackingProperties() { - return executorAsync().execute(getQueryTrackingPropertiesRequest(), QueryTrackingPropertiesEntity.class); + return executorAsync().execute(this::getQueryTrackingPropertiesRequest, QueryTrackingPropertiesEntity.class); } @Override public CompletableFuture setQueryTrackingProperties(final QueryTrackingPropertiesEntity properties) { - return executorAsync().execute(setQueryTrackingPropertiesRequest(properties), QueryTrackingPropertiesEntity.class); + return executorAsync().execute(() -> setQueryTrackingPropertiesRequest(properties), QueryTrackingPropertiesEntity.class); } @Override public CompletableFuture> getCurrentlyRunningQueries() { - return executorAsync().execute(getCurrentlyRunningQueriesRequest(), + return executorAsync().execute(this::getCurrentlyRunningQueriesRequest, constructListType(QueryEntity.class)); } @Override public CompletableFuture> getSlowQueries() { - return executorAsync().execute(getSlowQueriesRequest(), + return executorAsync().execute(this::getSlowQueriesRequest, constructListType(QueryEntity.class)); } @Override public CompletableFuture clearSlowQueries() { - return executorAsync().execute(clearSlowQueriesRequest(), Void.class); + return executorAsync().execute(this::clearSlowQueriesRequest, Void.class); } @Override public CompletableFuture killQuery(final String id) { - return executorAsync().execute(killQueryRequest(id), Void.class); + return executorAsync().execute(() -> killQueryRequest(id), Void.class); } @Override public CompletableFuture createAqlFunction( final String name, final String code, final AqlFunctionCreateOptions options) { - return executorAsync().execute(createAqlFunctionRequest(name, code, options), Void.class); + return executorAsync().execute(() -> createAqlFunctionRequest(name, code, options), Void.class); } @Override public CompletableFuture deleteAqlFunction(final String name, final AqlFunctionDeleteOptions options) { - return executorAsync().execute(deleteAqlFunctionRequest(name, options), deleteAqlFunctionResponseDeserializer()); + return executorAsync().execute(() -> deleteAqlFunctionRequest(name, options), deleteAqlFunctionResponseDeserializer()); } @Override public CompletableFuture> getAqlFunctions(final AqlFunctionGetOptions options) { - return executorAsync().execute(getAqlFunctionsRequest(options), getAqlFunctionsResponseDeserializer()); + return executorAsync().execute(() -> getAqlFunctionsRequest(options), getAqlFunctionsResponseDeserializer()); } @Override @@ -297,57 +294,57 @@ public CompletableFuture createGraph(final String name, final Itera @Override public CompletableFuture createGraph( final String name, final Iterable edgeDefinitions, final GraphCreateOptions options) { - return executorAsync().execute(createGraphRequest(name, edgeDefinitions, options), createGraphResponseDeserializer()); + return executorAsync().execute(() -> createGraphRequest(name, edgeDefinitions, options), createGraphResponseDeserializer()); } @Override public CompletableFuture> getGraphs() { - return executorAsync().execute(getGraphsRequest(), getGraphsResponseDeserializer()); + return executorAsync().execute(this::getGraphsRequest, getGraphsResponseDeserializer()); } @Override public CompletableFuture transaction(final String action, final Class type, final TransactionOptions options) { - return executorAsync().execute(transactionRequest(action, options), transactionResponseDeserializer(type)); + return executorAsync().execute(() -> transactionRequest(action, options), transactionResponseDeserializer(type)); } @Override public CompletableFuture beginStreamTransaction(StreamTransactionOptions options) { - return executorAsync().execute(beginStreamTransactionRequest(options), streamTransactionResponseDeserializer()); + return executorAsync().execute(() -> beginStreamTransactionRequest(options), streamTransactionResponseDeserializer()); } @Override public CompletableFuture abortStreamTransaction(String id) { - return executorAsync().execute(abortStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + return executorAsync().execute(() -> abortStreamTransactionRequest(id), streamTransactionResponseDeserializer()); } @Override public CompletableFuture getStreamTransaction(String id) { - return executorAsync().execute(getStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + return executorAsync().execute(() -> getStreamTransactionRequest(id), streamTransactionResponseDeserializer()); } @Override public CompletableFuture> getStreamTransactions() { - return executorAsync().execute(getStreamTransactionsRequest(), transactionsResponseDeserializer()); + return executorAsync().execute(this::getStreamTransactionsRequest, transactionsResponseDeserializer()); } @Override public CompletableFuture commitStreamTransaction(String id) { - return executorAsync().execute(commitStreamTransactionRequest(id), streamTransactionResponseDeserializer()); + return executorAsync().execute(() -> commitStreamTransactionRequest(id), streamTransactionResponseDeserializer()); } @Override public CompletableFuture getInfo() { - return executorAsync().execute(getInfoRequest(), getInfoResponseDeserializer()); + return executorAsync().execute(this::getInfoRequest, getInfoResponseDeserializer()); } @Override public CompletableFuture reloadRouting() { - return executorAsync().execute(reloadRoutingRequest(), Void.class); + return executorAsync().execute(this::reloadRoutingRequest, Void.class); } @Override public CompletableFuture> getViews() { - return executorAsync().execute(getViewsRequest(), getViewsResponseDeserializer()); + return executorAsync().execute(this::getViewsRequest, getViewsResponseDeserializer()); } @Override @@ -367,32 +364,32 @@ public SearchAliasAsync searchAlias(String name) { @Override public CompletableFuture createView(final String name, final ViewType type) { - return executorAsync().execute(createViewRequest(name, type), ViewEntity.class); + return executorAsync().execute(() -> createViewRequest(name, type), ViewEntity.class); } @Override public CompletableFuture createArangoSearch(final String name, final ArangoSearchCreateOptions options) { - return executorAsync().execute(createArangoSearchRequest(name, options), ViewEntity.class); + return executorAsync().execute(() -> createArangoSearchRequest(name, options), ViewEntity.class); } @Override public CompletableFuture createSearchAlias(String name, SearchAliasCreateOptions options) { - return executorAsync().execute(createSearchAliasRequest(name, options), ViewEntity.class); + return executorAsync().execute(() -> createSearchAliasRequest(name, options), ViewEntity.class); } @Override public CompletableFuture createSearchAnalyzer(SearchAnalyzer analyzer) { - return executorAsync().execute(createAnalyzerRequest(analyzer), SearchAnalyzer.class); + return executorAsync().execute(() -> createAnalyzerRequest(analyzer), SearchAnalyzer.class); } @Override public CompletableFuture getSearchAnalyzer(String name) { - return executorAsync().execute(getAnalyzerRequest(name), SearchAnalyzer.class); + return executorAsync().execute(() -> getAnalyzerRequest(name), SearchAnalyzer.class); } @Override public CompletableFuture> getSearchAnalyzers() { - return executorAsync().execute(getAnalyzersRequest(), getSearchAnalyzersResponseDeserializer()); + return executorAsync().execute(this::getAnalyzersRequest, getSearchAnalyzersResponseDeserializer()); } @Override @@ -402,7 +399,7 @@ public CompletableFuture deleteSearchAnalyzer(String name) { @Override public CompletableFuture deleteSearchAnalyzer(String name, AnalyzerDeleteOptions options) { - return executorAsync().execute(deleteAnalyzerRequest(name, options), Void.class); + return executorAsync().execute(() -> deleteAnalyzerRequest(name, options), Void.class); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java index f92147609..2321f20de 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoDatabaseImpl.java @@ -25,7 +25,6 @@ import com.arangodb.entity.arangosearch.analyzer.SearchAnalyzer; import com.arangodb.internal.cursor.ArangoCursorImpl; import com.arangodb.internal.net.HostHandle; -import com.arangodb.internal.util.DocumentUtil; import com.arangodb.model.*; import com.arangodb.model.arangosearch.AnalyzerDeleteOptions; import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; @@ -110,14 +109,12 @@ public Collection getCollections(final CollectionsReadOptions @Override public IndexEntity getIndex(final String id) { - DocumentUtil.validateIndexId(id); final String[] split = id.split("/"); return collection(split[0]).getIndex(split[1]); } @Override public String deleteIndex(final String id) { - DocumentUtil.validateIndexId(id); final String[] split = id.split("/"); return collection(split[0]).deleteIndex(split[1]); } diff --git a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java index f786f1b3c..d2484f317 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoEdgeCollectionAsyncImpl.java @@ -54,23 +54,23 @@ public CompletableFuture drop() { @Override public CompletableFuture drop(final EdgeCollectionDropOptions options) { - return executorAsync().execute(removeEdgeDefinitionRequest(options), Void.class); + return executorAsync().execute(() -> removeEdgeDefinitionRequest(options), Void.class); } @Override public CompletableFuture insertEdge(final Object value) { - return executorAsync().execute(insertEdgeRequest(value, new EdgeCreateOptions()), + return executorAsync().execute(() -> insertEdgeRequest(value, new EdgeCreateOptions()), insertEdgeResponseDeserializer()); } @Override public CompletableFuture insertEdge(final Object value, final EdgeCreateOptions options) { - return executorAsync().execute(insertEdgeRequest(value, options), insertEdgeResponseDeserializer()); + return executorAsync().execute(() -> insertEdgeRequest(value, options), insertEdgeResponseDeserializer()); } @Override public CompletableFuture getEdge(final String key, final Class type) { - return executorAsync().execute(getEdgeRequest(key, new GraphDocumentReadOptions()), + return executorAsync().execute(() -> getEdgeRequest(key, new GraphDocumentReadOptions()), getEdgeResponseDeserializer(type)) .exceptionally(e -> { // FIXME @@ -84,7 +84,7 @@ public CompletableFuture getEdge(final String key, final Class type) { @Override public CompletableFuture getEdge(final String key, final Class type, final GraphDocumentReadOptions options) { - return executorAsync().execute(getEdgeRequest(key, options), getEdgeResponseDeserializer(type)) + return executorAsync().execute(() -> getEdgeRequest(key, options), getEdgeResponseDeserializer(type)) .exceptionally(e -> { // FIXME if (LOGGER.isDebugEnabled()) { @@ -96,34 +96,34 @@ public CompletableFuture getEdge(final String key, final Class type, f @Override public CompletableFuture replaceEdge(final String key, final Object value) { - return executorAsync().execute(replaceEdgeRequest(key, value, new EdgeReplaceOptions()), + return executorAsync().execute(() -> replaceEdgeRequest(key, value, new EdgeReplaceOptions()), replaceEdgeResponseDeserializer()); } @Override public CompletableFuture replaceEdge(final String key, final Object value, final EdgeReplaceOptions options) { - return executorAsync().execute(replaceEdgeRequest(key, value, options), replaceEdgeResponseDeserializer()); + return executorAsync().execute(() -> replaceEdgeRequest(key, value, options), replaceEdgeResponseDeserializer()); } @Override public CompletableFuture updateEdge(final String key, final Object value) { - return executorAsync().execute(updateEdgeRequest(key, value, new EdgeUpdateOptions()), + return executorAsync().execute(() -> updateEdgeRequest(key, value, new EdgeUpdateOptions()), updateEdgeResponseDeserializer()); } @Override public CompletableFuture updateEdge(final String key, final Object value, final EdgeUpdateOptions options) { - return executorAsync().execute(updateEdgeRequest(key, value, options), updateEdgeResponseDeserializer()); + return executorAsync().execute(() -> updateEdgeRequest(key, value, options), updateEdgeResponseDeserializer()); } @Override public CompletableFuture deleteEdge(final String key) { - return executorAsync().execute(deleteEdgeRequest(key, new EdgeDeleteOptions()), Void.class); + return executorAsync().execute(() -> deleteEdgeRequest(key, new EdgeDeleteOptions()), Void.class); } @Override public CompletableFuture deleteEdge(final String key, final EdgeDeleteOptions options) { - return executorAsync().execute(deleteEdgeRequest(key, options), Void.class); + return executorAsync().execute(() -> deleteEdgeRequest(key, options), Void.class); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java b/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java index 2cd114bf6..3c865e3aa 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java @@ -27,6 +27,7 @@ import java.lang.reflect.Type; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; /** * @author Mark Vollmary @@ -38,32 +39,35 @@ public ArangoExecutorAsync(final CommunicationProtocol protocol, final ArangoCon super(protocol, config); } - public CompletableFuture execute(final InternalRequest request, final Type type) { - return execute(request, type, null); + public CompletableFuture execute(final Supplier requestSupplier, final Type type) { + return execute(requestSupplier, type, null); } - public CompletableFuture execute(final InternalRequest request, final Type type, final HostHandle hostHandle) { - return execute(request, response -> createResult(type, response), hostHandle); + public CompletableFuture execute(final Supplier requestSupplier, final Type type, final HostHandle hostHandle) { + return execute(requestSupplier, response -> createResult(type, response), hostHandle); } - public CompletableFuture execute(final InternalRequest request, final ResponseDeserializer responseDeserializer) { - return execute(request, responseDeserializer, null); + public CompletableFuture execute(final Supplier requestSupplier, final ResponseDeserializer responseDeserializer) { + return execute(requestSupplier, responseDeserializer, null); } public CompletableFuture execute( - final InternalRequest request, + final Supplier requestSupplier, final ResponseDeserializer responseDeserializer, final HostHandle hostHandle) { - return protocol.executeAsync(interceptRequest(request), hostHandle) - .handle((r, e) -> { - if (e != null) { - throw ArangoDBException.of(e); - } else { - interceptResponse(r); - return responseDeserializer.deserialize(r); - } - }); + return CompletableFuture.completedFuture(requestSupplier) + .thenApply(Supplier::get) + .thenCompose(request -> protocol.executeAsync(interceptRequest(request), hostHandle) + .handle((r, e) -> { + if (e != null) { + throw ArangoDBException.of(e); + } else { + interceptResponse(r); + return responseDeserializer.deserialize(r); + } + }) + ); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java index b70ace6e0..9155a3ba8 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoGraphAsyncImpl.java @@ -74,22 +74,22 @@ public CompletableFuture create(final Iterable edge @Override public CompletableFuture drop() { - return executorAsync().execute(dropRequest(), Void.class); + return executorAsync().execute(this::dropRequest, Void.class); } @Override public CompletableFuture drop(final boolean dropCollections) { - return executorAsync().execute(dropRequest(dropCollections), Void.class); + return executorAsync().execute(() -> dropRequest(dropCollections), Void.class); } @Override public CompletableFuture getInfo() { - return executorAsync().execute(getInfoRequest(), getInfoResponseDeserializer()); + return executorAsync().execute(this::getInfoRequest, getInfoResponseDeserializer()); } @Override public CompletableFuture> getVertexCollections() { - return executorAsync().execute(getVertexCollectionsRequest(), getVertexCollectionsResponseDeserializer()); + return executorAsync().execute(this::getVertexCollectionsRequest, getVertexCollectionsResponseDeserializer()); } @Override @@ -99,7 +99,7 @@ public CompletableFuture addVertexCollection(final String name) { @Override public CompletableFuture addVertexCollection(final String name, final VertexCollectionCreateOptions options) { - return executorAsync().execute(addVertexCollectionRequest(name, options), addVertexCollectionResponseDeserializer()); + return executorAsync().execute(() -> addVertexCollectionRequest(name, options), addVertexCollectionResponseDeserializer()); } @Override @@ -114,12 +114,12 @@ public ArangoEdgeCollectionAsync edgeCollection(final String name) { @Override public CompletableFuture> getEdgeDefinitions() { - return executorAsync().execute(getEdgeDefinitionsRequest(), getEdgeDefinitionsDeserializer()); + return executorAsync().execute(this::getEdgeDefinitionsRequest, getEdgeDefinitionsDeserializer()); } @Override public CompletableFuture addEdgeDefinition(final EdgeDefinition definition) { - return executorAsync().execute(addEdgeDefinitionRequest(definition), addEdgeDefinitionResponseDeserializer()); + return executorAsync().execute(() -> addEdgeDefinitionRequest(definition), addEdgeDefinitionResponseDeserializer()); } @Override @@ -129,7 +129,7 @@ public CompletableFuture replaceEdgeDefinition(final EdgeDefinition @Override public CompletableFuture replaceEdgeDefinition(final EdgeDefinition definition, final ReplaceEdgeDefinitionOptions options) { - return executorAsync().execute(replaceEdgeDefinitionRequest(definition, options), replaceEdgeDefinitionResponseDeserializer()); + return executorAsync().execute(() -> replaceEdgeDefinitionRequest(definition, options), replaceEdgeDefinitionResponseDeserializer()); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java index 1f7f3b8e9..5dcc07805 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoSearchAsyncImpl.java @@ -64,17 +64,17 @@ public CompletableFuture exists() { @Override public CompletableFuture drop() { - return executorAsync().execute(dropRequest(), Void.class); + return executorAsync().execute(this::dropRequest, Void.class); } @Override public CompletableFuture rename(final String newName) { - return executorAsync().execute(renameRequest(newName), ViewEntity.class); + return executorAsync().execute(() -> renameRequest(newName), ViewEntity.class); } @Override public CompletableFuture getInfo() { - return executorAsync().execute(getInfoRequest(), ViewEntity.class); + return executorAsync().execute(this::getInfoRequest, ViewEntity.class); } @Override @@ -89,17 +89,17 @@ public CompletableFuture create(final ArangoSearchCreateOptions opti @Override public CompletableFuture getProperties() { - return executorAsync().execute(getPropertiesRequest(), ArangoSearchPropertiesEntity.class); + return executorAsync().execute(this::getPropertiesRequest, ArangoSearchPropertiesEntity.class); } @Override public CompletableFuture updateProperties(final ArangoSearchPropertiesOptions options) { - return executorAsync().execute(updatePropertiesRequest(options), ArangoSearchPropertiesEntity.class); + return executorAsync().execute(() -> updatePropertiesRequest(options), ArangoSearchPropertiesEntity.class); } @Override public CompletableFuture replaceProperties(final ArangoSearchPropertiesOptions options) { - return executorAsync().execute(replacePropertiesRequest(options), ArangoSearchPropertiesEntity.class); + return executorAsync().execute(() -> replacePropertiesRequest(options), ArangoSearchPropertiesEntity.class); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java index 0d946bd67..e11f612fe 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoVertexCollectionAsyncImpl.java @@ -56,23 +56,23 @@ public CompletableFuture drop() { @Override public CompletableFuture drop(final VertexCollectionDropOptions options) { - return executorAsync().execute(dropRequest(options), Void.class); + return executorAsync().execute(() -> dropRequest(options), Void.class); } @Override public CompletableFuture insertVertex(final Object value) { - return executorAsync().execute(insertVertexRequest(value, new VertexCreateOptions()), + return executorAsync().execute(() -> insertVertexRequest(value, new VertexCreateOptions()), insertVertexResponseDeserializer()); } @Override public CompletableFuture insertVertex(final Object value, final VertexCreateOptions options) { - return executorAsync().execute(insertVertexRequest(value, options), insertVertexResponseDeserializer()); + return executorAsync().execute(() -> insertVertexRequest(value, options), insertVertexResponseDeserializer()); } @Override public CompletableFuture getVertex(final String key, final Class type) { - return executorAsync().execute(getVertexRequest(key, new GraphDocumentReadOptions()), + return executorAsync().execute(() -> getVertexRequest(key, new GraphDocumentReadOptions()), getVertexResponseDeserializer(type)) .exceptionally(e -> { // FIXME @@ -86,7 +86,7 @@ public CompletableFuture getVertex(final String key, final Class type) @Override public CompletableFuture getVertex(final String key, final Class type, final GraphDocumentReadOptions options) { - return executorAsync().execute(getVertexRequest(key, options), getVertexResponseDeserializer(type)) + return executorAsync().execute(() -> getVertexRequest(key, options), getVertexResponseDeserializer(type)) .exceptionally(e -> { // FIXME if (LOGGER.isDebugEnabled()) { @@ -98,34 +98,34 @@ public CompletableFuture getVertex(final String key, final Class type, @Override public CompletableFuture replaceVertex(final String key, final Object value) { - return executorAsync().execute(replaceVertexRequest(key, value, new VertexReplaceOptions()), + return executorAsync().execute(() -> replaceVertexRequest(key, value, new VertexReplaceOptions()), replaceVertexResponseDeserializer()); } @Override public CompletableFuture replaceVertex(final String key, final Object value, final VertexReplaceOptions options) { - return executorAsync().execute(replaceVertexRequest(key, value, options), replaceVertexResponseDeserializer()); + return executorAsync().execute(() -> replaceVertexRequest(key, value, options), replaceVertexResponseDeserializer()); } @Override public CompletableFuture updateVertex(final String key, final Object value) { - return executorAsync().execute(updateVertexRequest(key, value, new VertexUpdateOptions()), + return executorAsync().execute(() -> updateVertexRequest(key, value, new VertexUpdateOptions()), updateVertexResponseDeserializer()); } @Override public CompletableFuture updateVertex(final String key, final Object value, final VertexUpdateOptions options) { - return executorAsync().execute(updateVertexRequest(key, value, options), updateVertexResponseDeserializer()); + return executorAsync().execute(() -> updateVertexRequest(key, value, options), updateVertexResponseDeserializer()); } @Override public CompletableFuture deleteVertex(final String key) { - return executorAsync().execute(deleteVertexRequest(key, new VertexDeleteOptions()), Void.class); + return executorAsync().execute(() -> deleteVertexRequest(key, new VertexDeleteOptions()), Void.class); } @Override public CompletableFuture deleteVertex(final String key, final VertexDeleteOptions options) { - return executorAsync().execute(deleteVertexRequest(key, options), Void.class); + return executorAsync().execute(() -> deleteVertexRequest(key, options), Void.class); } } diff --git a/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java b/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java index 4e4643544..8af8837f6 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/ArangoViewAsyncImpl.java @@ -60,17 +60,17 @@ public CompletableFuture exists() { @Override public CompletableFuture drop() { - return executorAsync().execute(dropRequest(), Void.class); + return executorAsync().execute(this::dropRequest, Void.class); } @Override public CompletableFuture rename(final String newName) { - return executorAsync().execute(renameRequest(newName), ViewEntity.class); + return executorAsync().execute(() -> renameRequest(newName), ViewEntity.class); } @Override public CompletableFuture getInfo() { - return executorAsync().execute(getInfoRequest(), ViewEntity.class); + return executorAsync().execute(this::getInfoRequest, ViewEntity.class); } } diff --git a/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java b/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java index ef02daded..faa70d61a 100644 --- a/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/SearchAliasAsyncImpl.java @@ -64,17 +64,17 @@ public CompletableFuture exists() { @Override public CompletableFuture drop() { - return executorAsync().execute(dropRequest(), Void.class); + return executorAsync().execute(this::dropRequest, Void.class); } @Override public CompletableFuture rename(final String newName) { - return executorAsync().execute(renameRequest(newName), ViewEntity.class); + return executorAsync().execute(() -> renameRequest(newName), ViewEntity.class); } @Override public CompletableFuture getInfo() { - return executorAsync().execute(getInfoRequest(), ViewEntity.class); + return executorAsync().execute(this::getInfoRequest, ViewEntity.class); } @Override @@ -89,17 +89,17 @@ public CompletableFuture create(final SearchAliasCreateOptions optio @Override public CompletableFuture getProperties() { - return executorAsync().execute(getPropertiesRequest(), SearchAliasPropertiesEntity.class); + return executorAsync().execute(this::getPropertiesRequest, SearchAliasPropertiesEntity.class); } @Override public CompletableFuture updateProperties(final SearchAliasPropertiesOptions options) { - return executorAsync().execute(updatePropertiesRequest(options), SearchAliasPropertiesEntity.class); + return executorAsync().execute(() -> updatePropertiesRequest(options), SearchAliasPropertiesEntity.class); } @Override public CompletableFuture replaceProperties(final SearchAliasPropertiesOptions options) { - return executorAsync().execute(replacePropertiesRequest(options), SearchAliasPropertiesEntity.class); + return executorAsync().execute(() -> replacePropertiesRequest(options), SearchAliasPropertiesEntity.class); } } diff --git a/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorAsyncImpl.java b/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorAsyncImpl.java index d637df84c..15c21b4f6 100644 --- a/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorAsyncImpl.java +++ b/core/src/main/java/com/arangodb/internal/cursor/ArangoCursorAsyncImpl.java @@ -29,19 +29,19 @@ public ArangoCursorAsyncImpl( @Override public CompletableFuture> nextBatch() { if (Boolean.TRUE.equals(hasMore())) { - return executorAsync().execute(queryNextRequest(), db.cursorEntityDeserializer(getType()), hostHandle) + return executorAsync().execute(this::queryNextRequest, db.cursorEntityDeserializer(getType()), hostHandle) .thenApply(r -> new ArangoCursorAsyncImpl<>(db, r, getType(), hostHandle, allowRetry())); } else { - return CompletableFuture.supplyAsync(() -> { - throw new NoSuchElementException(); - }); + CompletableFuture> cf = new CompletableFuture<>(); + cf.completeExceptionally(new NoSuchElementException()); + return cf; } } @Override public CompletableFuture close() { if (getId() != null && (allowRetry() || Boolean.TRUE.equals(hasMore()))) { - return executorAsync().execute(queryCloseRequest(), Void.class, hostHandle); + return executorAsync().execute(this::queryCloseRequest, Void.class, hostHandle); } else { return CompletableFuture.completedFuture(null); } diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java index 63f5e2efa..df422a94b 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionAsyncTest.java @@ -504,7 +504,7 @@ void getDocumentNotFoundOptionsNull(ArangoCollectionAsync collection) throws Exe @ParameterizedTest(name = "{index}") @MethodSource("asyncCols") void getDocumentWrongKey(ArangoCollectionAsync collection) { - Throwable thrown = catchThrowable(() -> collection.getDocument("no/no", BaseDocument.class).get()); + Throwable thrown = catchThrowable(() -> collection.getDocument("no/no", BaseDocument.class).get()).getCause(); assertThat(thrown).isInstanceOf(ArangoDBException.class); } diff --git a/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java b/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java index 39bb4eeca..d2d68759c 100644 --- a/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java +++ b/driver/src/test/java/com/arangodb/ArangoViewAsyncTest.java @@ -58,7 +58,7 @@ void create(ArangoDatabaseAsync db) throws ExecutionException, InterruptedExcept void createWithNotNormalizedName(ArangoDatabaseAsync db) { assumeTrue(supportsExtendedNames()); final String name = "view-\u006E\u0303\u00f1"; - Throwable thrown = catchThrowable(() -> db.createView(name, ViewType.ARANGO_SEARCH)); + Throwable thrown = catchThrowable(() -> db.createView(name, ViewType.ARANGO_SEARCH).get()).getCause(); assertThat(thrown) .isInstanceOf(ArangoDBException.class) .hasMessageContaining("normalized") From 9c88d8581bb9ad596b9067145873699bc86adcc4 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 25 Oct 2023 16:06:22 +0200 Subject: [PATCH 62/62] async downstream executor (DE-697) --- core/src/main/java/com/arangodb/ArangoDB.java | 5 - .../internal/ArangoExecutorAsync.java | 31 +++-- .../internal/InternalArangoDBBuilder.java | 20 +++ .../internal/config/ArangoConfig.java | 10 ++ .../com/arangodb/ConsumerThreadAsyncTest.java | 118 ++++++++++++++++++ .../com/arangodb/http/HttpConnection.java | 2 +- .../arangodb/vst/internal/VstConnection.java | 9 +- 7 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 driver/src/test/java/com/arangodb/ConsumerThreadAsyncTest.java diff --git a/core/src/main/java/com/arangodb/ArangoDB.java b/core/src/main/java/com/arangodb/ArangoDB.java index 57552df9f..636206962 100644 --- a/core/src/main/java/com/arangodb/ArangoDB.java +++ b/core/src/main/java/com/arangodb/ArangoDB.java @@ -340,11 +340,6 @@ public interface ArangoDB extends ArangoSerdeAccessor { */ class Builder extends InternalArangoDBBuilder { - public Builder protocol(final Protocol protocol) { - config.setProtocol(protocol); - return this; - } - /** * Returns an instance of {@link ArangoDB}. * diff --git a/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java b/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java index 3c865e3aa..88c50ac2a 100644 --- a/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java +++ b/core/src/main/java/com/arangodb/internal/ArangoExecutorAsync.java @@ -27,6 +27,8 @@ import java.lang.reflect.Type; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Function; import java.util.function.Supplier; /** @@ -34,9 +36,11 @@ * @author Michele Rastelli */ public class ArangoExecutorAsync extends ArangoExecutor { + private final Executor downstreamExecutor; public ArangoExecutorAsync(final CommunicationProtocol protocol, final ArangoConfig config) { super(protocol, config); + downstreamExecutor = config.getAsyncExecutor(); } public CompletableFuture execute(final Supplier requestSupplier, final Type type) { @@ -56,18 +60,23 @@ public CompletableFuture execute( final ResponseDeserializer responseDeserializer, final HostHandle hostHandle) { - return CompletableFuture.completedFuture(requestSupplier) + CompletableFuture cf = CompletableFuture.completedFuture(requestSupplier) .thenApply(Supplier::get) - .thenCompose(request -> protocol.executeAsync(interceptRequest(request), hostHandle) - .handle((r, e) -> { - if (e != null) { - throw ArangoDBException.of(e); - } else { - interceptResponse(r); - return responseDeserializer.deserialize(r); - } - }) - ); + .thenCompose(request -> protocol.executeAsync(interceptRequest(request), hostHandle)) + .handle((r, e) -> { + if (e != null) { + throw ArangoDBException.of(e); + } else { + interceptResponse(r); + return responseDeserializer.deserialize(r); + } + }); + + if (downstreamExecutor != null) { + return cf.thenApplyAsync(Function.identity(), downstreamExecutor); + } else { + return cf; + } } } diff --git a/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java b/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java index 13b73bb99..68199fe67 100644 --- a/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java +++ b/core/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.ServiceLoader; +import java.util.concurrent.Executor; /** @@ -53,6 +54,12 @@ public T loadProperties(final ArangoConfigProperties properties) { return (T) this; } + @SuppressWarnings("unchecked") + public T protocol(final Protocol protocol) { + config.setProtocol(protocol); + return (T) this; + } + /** * Adds a host to connect to. Multiple hosts can be added to provide fallbacks. * @@ -289,6 +296,19 @@ public T serde(final ArangoSerde serde) { return (T) this; } + /** + * Sets the downstream async executor that will be used to consume the responses of the async API, that are returned + * as {@link java.util.concurrent.CompletableFuture} + * + * @param executor async downstream executor + * @return {@link ArangoDB.Builder} + */ + @SuppressWarnings("unchecked") + public T asyncExecutor(final Executor executor) { + config.setAsyncExecutor(executor); + return (T) this; + } + protected ProtocolProvider protocolProvider(Protocol protocol) { ServiceLoader loader = ServiceLoader.load(ProtocolProvider.class); for (ProtocolProvider p : loader) { diff --git a/core/src/main/java/com/arangodb/internal/config/ArangoConfig.java b/core/src/main/java/com/arangodb/internal/config/ArangoConfig.java index 5afdcc9d8..5afe71616 100644 --- a/core/src/main/java/com/arangodb/internal/config/ArangoConfig.java +++ b/core/src/main/java/com/arangodb/internal/config/ArangoConfig.java @@ -18,6 +18,7 @@ import javax.net.ssl.SSLContext; import java.util.*; +import java.util.concurrent.Executor; import java.util.stream.Collectors; public class ArangoConfig { @@ -41,6 +42,7 @@ public class ArangoConfig { private ArangoSerde userDataSerde; private Integer responseQueueTimeSamples; private Module protocolModule; + private Executor asyncExecutor; private static final Logger LOG = LoggerFactory.getLogger(ArangoConfig.class); @@ -275,4 +277,12 @@ public void setResponseQueueTimeSamples(Integer responseQueueTimeSamples) { public void setProtocolModule(Module m) { protocolModule = m; } + + public Executor getAsyncExecutor() { + return asyncExecutor; + } + + public void setAsyncExecutor(Executor asyncExecutor) { + this.asyncExecutor = asyncExecutor; + } } diff --git a/driver/src/test/java/com/arangodb/ConsumerThreadAsyncTest.java b/driver/src/test/java/com/arangodb/ConsumerThreadAsyncTest.java new file mode 100644 index 000000000..c58a4a815 --- /dev/null +++ b/driver/src/test/java/com/arangodb/ConsumerThreadAsyncTest.java @@ -0,0 +1,118 @@ +package com.arangodb; + +import com.arangodb.config.ArangoConfigProperties; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ConsumerThreadAsyncTest extends BaseJunit5 { + + private volatile Thread thread; + + private void setThread() { + thread = Thread.currentThread(); + } + + private void sleep() { + try { + Thread.sleep(3_000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @ParameterizedTest + @EnumSource(Protocol.class) + @Disabled + void defaultConsumerThread(Protocol protocol) throws ExecutionException, InterruptedException { + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ArangoConfigProperties.fromFile()) + .protocol(protocol) + .build() + .async(); + + adb.getVersion() + .thenAccept(it -> setThread()) + .get(); + + adb.shutdown(); + + if (Protocol.VST.equals(protocol)) { + assertThat(thread.getName()).startsWith("adb-vst-"); + } else { + assertThat(thread.getName()).startsWith("adb-http-"); + } + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void customConsumerExecutor(Protocol protocol) throws ExecutionException, InterruptedException { + ExecutorService es = Executors.newCachedThreadPool(r -> { + Thread t = Executors.defaultThreadFactory().newThread(r); + t.setName("custom-" + UUID.randomUUID()); + return t; + }); + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ArangoConfigProperties.fromFile()) + .protocol(protocol) + .asyncExecutor(es) + .build() + .async(); + + adb.getVersion() + .thenAccept(it -> setThread()) + .get(); + + adb.shutdown(); + es.shutdown(); + assertThat(thread.getName()).startsWith("custom-"); + } + + /** + * Generates warns from Vert.x BlockedThreadChecker + */ + @ParameterizedTest + @EnumSource(Protocol.class) + @Disabled + void sleepOnDefaultConsumerThread(Protocol protocol) throws ExecutionException, InterruptedException { + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ArangoConfigProperties.fromFile()) + .protocol(protocol) + .maxConnections(1) + .build() + .async(); + + adb.getVersion() + .thenAccept(it -> sleep()) + .get(); + + adb.shutdown(); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void nestedRequests(Protocol protocol) throws ExecutionException, InterruptedException { + ArangoDBAsync adb = new ArangoDB.Builder() + .loadProperties(ArangoConfigProperties.fromFile()) + .protocol(protocol) + .maxConnections(1) + .build() + .async(); + + adb.getVersion() + .thenCompose(it -> adb.getVersion()) + .thenCompose(it -> adb.getVersion()) + .thenCompose(it -> adb.getVersion()) + .get(); + + adb.shutdown(); + } + +} diff --git a/http/src/main/java/com/arangodb/http/HttpConnection.java b/http/src/main/java/com/arangodb/http/HttpConnection.java index 6d5833465..d18850d39 100644 --- a/http/src/main/java/com/arangodb/http/HttpConnection.java +++ b/http/src/main/java/com/arangodb/http/HttpConnection.java @@ -90,7 +90,7 @@ private static String getUserAgent() { timeout = config.getTimeout(); vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(true).setEventLoopPoolSize(1)); vertx.runOnContext(e -> { - Thread.currentThread().setName("adb-eventloop-" + THREAD_COUNT.getAndIncrement()); + Thread.currentThread().setName("adb-http-" + THREAD_COUNT.getAndIncrement()); auth = new UsernamePasswordCredentials( config.getUser(), Optional.ofNullable(config.getPassword()).orElse("") ).toHttpAuthorization(); diff --git a/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java b/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java index 3b96c54eb..8d9339463 100644 --- a/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java +++ b/vst/src/main/java/com/arangodb/vst/internal/VstConnection.java @@ -48,6 +48,7 @@ import java.util.Date; import java.util.Map; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** @@ -55,6 +56,7 @@ */ public abstract class VstConnection implements Connection { private static final Logger LOGGER = LoggerFactory.getLogger(VstConnection.class); + private static final AtomicInteger THREAD_COUNT = new AtomicInteger(); private static final byte[] PROTOCOL_HEADER = "VST/1.0\r\n\r\n".getBytes(); protected final MessageStore messageStore = new MessageStore(); protected final Integer timeout; @@ -171,7 +173,12 @@ public synchronized void open() throws IOException { } sendProtocolHeader(); - executor = Executors.newSingleThreadExecutor(); + executor = Executors.newSingleThreadExecutor(r -> { + Thread t = Executors.defaultThreadFactory().newThread(r); + t.setDaemon(true); + t.setName("adb-vst-" + THREAD_COUNT.getAndIncrement()); + return t; + }); executor.submit((Callable) () -> { LOGGER.debug("[" + connectionName + "]: Start Callable");