From 3c0a08dd1a1f027c50f46938fe2296bb110bec5e Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 2 Dec 2022 15:20:28 +0100 Subject: [PATCH 01/20] transitive deps relocation --- .../{ => META-INF}/vertx/vertx-version.txt | 0 pom.xml | 1 + resilience-tests/pom.xml | 2 +- shaded/pom.xml | 142 ++++++++++++++++++ .../META-INF/vertx/vertx-version.txt | 1 + 5 files changed, 145 insertions(+), 1 deletion(-) rename driver/src/main/resources/{ => META-INF}/vertx/vertx-version.txt (100%) create mode 100644 shaded/pom.xml create mode 100644 shaded/src/main/resources/META-INF/vertx/vertx-version.txt diff --git a/driver/src/main/resources/vertx/vertx-version.txt b/driver/src/main/resources/META-INF/vertx/vertx-version.txt similarity index 100% rename from driver/src/main/resources/vertx/vertx-version.txt rename to driver/src/main/resources/META-INF/vertx/vertx-version.txt diff --git a/pom.xml b/pom.xml index fb96cbcb0..9d3220034 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ driver resilience-tests + shaded pom diff --git a/resilience-tests/pom.xml b/resilience-tests/pom.xml index d22ebc321..d25bb3f50 100644 --- a/resilience-tests/pom.xml +++ b/resilience-tests/pom.xml @@ -27,7 +27,7 @@ com.arangodb arangodb-java-driver - ${version} + ${project.version} com.arangodb diff --git a/shaded/pom.xml b/shaded/pom.xml new file mode 100644 index 000000000..93816dafd --- /dev/null +++ b/shaded/pom.xml @@ -0,0 +1,142 @@ + + + 4.0.0 + + com.arangodb + arangodb-java-driver-parent + 7.0.0-SNAPSHOT + + + arangodb-java-driver-shaded + arangodb-java-driver-shaded + ArangoDB Java Driver Shaded + https://github.com/arangodb/arangodb-java-driver + + + UTF-8 + + + + + com.arangodb + arangodb-java-driver + ${project.version} + + + com.arangodb + jackson-dataformat-velocypack + 3.1.0 + + + org.slf4j + slf4j-api + 1.7.36 + + + jakarta.json.bind + jakarta.json.bind-api + 2.0.0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + true + 1.8 + 1.8 + + + + maven-shade-plugin + 3.3.0 + + + shade-core-dependencies + package + + shade + + + true + true + + + org.slf4j:slf4j-api + jakarta.json.bind:jakarta.json.bind-api + + + + + + io.netty + com.arangodb.shaded.netty + + + com.fasterxml.jackson + com.arangodb.shaded.fasterxml.jackson + + + io.vertx + com.arangodb.shaded.vertx + + + + + com.arangodb:velocypack + + META-INF/** + + + + com.arangodb:arangodb-java-driver + + META-INF/** + + + + com.arangodb:jackson-dataformat-velocypack + + META-INF/** + + + + com.fasterxml.jackson.core:* + + META-INF/** + module-info.class + + + + io.netty:* + + META-INF/** + module-info.class + + + + io.vertx:* + + *.adoc + examples/** + META-INF/** + module-info.class + + + + + + + + + + + \ No newline at end of file diff --git a/shaded/src/main/resources/META-INF/vertx/vertx-version.txt b/shaded/src/main/resources/META-INF/vertx/vertx-version.txt new file mode 100644 index 000000000..a6695ff98 --- /dev/null +++ b/shaded/src/main/resources/META-INF/vertx/vertx-version.txt @@ -0,0 +1 @@ +4.3.4 \ No newline at end of file From a339323fff54bbef78c3a65cca746c49d6d69c09 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 5 Dec 2022 15:17:06 +0100 Subject: [PATCH 02/20] split classes that do not must be shaded --- .gitignore | 1 + .../internal/serde/InternalSerde.java | 3 +-- .../internal/serde/InternalSerdeImpl.java | 25 ++++++++++++++++--- .../internal/serde/JacksonSerdeImpl.java | 16 +++++++++--- .../serde/UserJsonMapperProvider.java | 15 +++++++++++ .../internal/serde/UserMapperProvider.java | 21 ++++++++++++++++ .../serde/UserVPackMapperProvider.java | 16 ++++++++++++ .../java/com/arangodb/serde/JacksonSerde.java | 6 ++--- resilience-tests/pom.xml | 2 +- shaded/pom.xml | 15 +++++++++-- .../META-INF/vertx/vertx-version.txt | 1 - 11 files changed, 104 insertions(+), 17 deletions(-) create mode 100644 driver/src/main/java/com/arangodb/internal/serde/UserJsonMapperProvider.java create mode 100644 driver/src/main/java/com/arangodb/internal/serde/UserMapperProvider.java create mode 100644 driver/src/main/java/com/arangodb/internal/serde/UserVPackMapperProvider.java delete mode 100644 shaded/src/main/resources/META-INF/vertx/vertx-version.txt diff --git a/.gitignore b/.gitignore index 8fcbc5c8a..11ee1822e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ test-results-native .flattened-pom.xml /resilience-tests/bin/toxiproxy-server-linux-amd64 +dependency-reduced-pom.xml diff --git a/driver/src/main/java/com/arangodb/internal/serde/InternalSerde.java b/driver/src/main/java/com/arangodb/internal/serde/InternalSerde.java index 4a6087981..2d0543093 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/InternalSerde.java +++ b/driver/src/main/java/com/arangodb/internal/serde/InternalSerde.java @@ -2,13 +2,12 @@ import com.arangodb.serde.ArangoSerde; import com.arangodb.ContentType; -import com.arangodb.serde.JacksonSerde; import com.fasterxml.jackson.databind.JsonNode; import java.lang.reflect.Type; import java.util.Collection; -public interface InternalSerde extends JacksonSerde { +public interface InternalSerde extends ArangoSerde { /** * Creates a new InternalSerde with default settings for the specified data type. diff --git a/driver/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java b/driver/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java index 9ade5dbbf..6e3e4c349 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java +++ b/driver/src/main/java/com/arangodb/internal/serde/InternalSerdeImpl.java @@ -7,6 +7,8 @@ import com.arangodb.util.RawBytes; import com.arangodb.util.RawJson; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -18,26 +20,37 @@ import static com.arangodb.internal.serde.SerdeUtils.checkSupportedJacksonVersion; -final class InternalSerdeImpl extends JacksonSerdeImpl implements InternalSerde { +final class InternalSerdeImpl implements InternalSerde { static { checkSupportedJacksonVersion(); } private final ArangoSerde userSerde; + private final ObjectMapper mapper; InternalSerdeImpl(final ObjectMapper mapper, final ArangoSerde userSerde) { - super(mapper); + this.mapper = mapper; this.userSerde = userSerde; + mapper.deactivateDefaultTyping(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.registerModule(InternalModule.INSTANCE.get()); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.deactivateDefaultTyping(); mapper.setAnnotationIntrospector(new InternalAnnotationIntrospector( new UserDataSerializer(this), new UserDataDeserializer(this) )); } + @Override + public byte[] serialize(final Object value) { + try { + return mapper.writeValueAsBytes(value); + } catch (JsonProcessingException e) { + throw new ArangoDBException(e); + } + } + @Override public String toJsonString(final byte[] content) { try { @@ -140,7 +153,11 @@ public T deserialize(final byte[] content, final Type type) { if (content == null) { return null; } - return super.deserialize(content, type); + try { + return mapper.readerFor(mapper.constructType(type)).readValue(content); + } catch (IOException e) { + throw new ArangoDBException(e); + } } private boolean isManagedClass(Class clazz) { diff --git a/driver/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java b/driver/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java index cf24b9f01..1542929e7 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java +++ b/driver/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java @@ -10,14 +10,22 @@ import java.lang.reflect.Type; import java.util.function.Consumer; -public class JacksonSerdeImpl implements JacksonSerde { +/** + * Not shaded in arangodb-java-driver-shaded. + */ +public final class JacksonSerdeImpl implements JacksonSerde { - protected final ObjectMapper mapper; + private final ObjectMapper mapper; public JacksonSerdeImpl(final ObjectMapper mapper) { this.mapper = mapper; - mapper.deactivateDefaultTyping(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + try { + mapper.deactivateDefaultTyping(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } catch (Exception e) { + // to be safe in case the provided Jackson version does not support the methods above + e.printStackTrace(); + } } @Override diff --git a/driver/src/main/java/com/arangodb/internal/serde/UserJsonMapperProvider.java b/driver/src/main/java/com/arangodb/internal/serde/UserJsonMapperProvider.java new file mode 100644 index 000000000..4ff51de04 --- /dev/null +++ b/driver/src/main/java/com/arangodb/internal/serde/UserJsonMapperProvider.java @@ -0,0 +1,15 @@ +package com.arangodb.internal.serde; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Not shaded in arangodb-java-driver-shaded. + */ +enum UserJsonMapperProvider implements MapperProvider { + INSTANCE; + + @Override + public ObjectMapper get() { + return new ObjectMapper(); + } +} diff --git a/driver/src/main/java/com/arangodb/internal/serde/UserMapperProvider.java b/driver/src/main/java/com/arangodb/internal/serde/UserMapperProvider.java new file mode 100644 index 000000000..3fe781ca9 --- /dev/null +++ b/driver/src/main/java/com/arangodb/internal/serde/UserMapperProvider.java @@ -0,0 +1,21 @@ +package com.arangodb.internal.serde; + +import com.arangodb.ContentType; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.function.Supplier; + +/** + * Not shaded in arangodb-java-driver-shaded. + */ +public interface UserMapperProvider extends Supplier { + static ObjectMapper of(final ContentType contentType) { + if (contentType == ContentType.JSON) { + return UserJsonMapperProvider.INSTANCE.get(); + } else if (contentType == ContentType.VPACK) { + return UserVPackMapperProvider.INSTANCE.get(); + } else { + throw new IllegalArgumentException("Unexpected value: " + contentType); + } + } +} diff --git a/driver/src/main/java/com/arangodb/internal/serde/UserVPackMapperProvider.java b/driver/src/main/java/com/arangodb/internal/serde/UserVPackMapperProvider.java new file mode 100644 index 000000000..64115cd96 --- /dev/null +++ b/driver/src/main/java/com/arangodb/internal/serde/UserVPackMapperProvider.java @@ -0,0 +1,16 @@ +package com.arangodb.internal.serde; + +import com.arangodb.jackson.dataformat.velocypack.VPackMapper; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Not shaded in arangodb-java-driver-shaded. + */ +enum UserVPackMapperProvider implements MapperProvider { + INSTANCE; + + @Override + public ObjectMapper get() { + return new VPackMapper(); + } +} diff --git a/driver/src/main/java/com/arangodb/serde/JacksonSerde.java b/driver/src/main/java/com/arangodb/serde/JacksonSerde.java index 2a642c2ac..8125e5910 100644 --- a/driver/src/main/java/com/arangodb/serde/JacksonSerde.java +++ b/driver/src/main/java/com/arangodb/serde/JacksonSerde.java @@ -2,13 +2,13 @@ import com.arangodb.ContentType; import com.arangodb.internal.serde.JacksonSerdeImpl; -import com.arangodb.internal.serde.MapperProvider; +import com.arangodb.internal.serde.UserMapperProvider; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.function.Consumer; /** - * User data serde based on Jackson Databind. + * User data serde based on Jackson Databind. Not shaded in arangodb-java-driver-shaded. */ public interface JacksonSerde extends ArangoSerde { @@ -19,7 +19,7 @@ public interface JacksonSerde extends ArangoSerde { * @return the created JacksonSerde */ static JacksonSerde of(final ContentType contentType) { - return create(MapperProvider.of(contentType)); + return create(UserMapperProvider.of(contentType)); } /** diff --git a/resilience-tests/pom.xml b/resilience-tests/pom.xml index d25bb3f50..98fa04e02 100644 --- a/resilience-tests/pom.xml +++ b/resilience-tests/pom.xml @@ -32,7 +32,7 @@ com.arangodb jackson-dataformat-velocypack - 3.0.1 + 3.1.0 eu.rekawek.toxiproxy diff --git a/shaded/pom.xml b/shaded/pom.xml index 93816dafd..b5d32c922 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -55,7 +55,7 @@ maven-shade-plugin - 3.3.0 + 3.4.1 shade-core-dependencies @@ -83,6 +83,17 @@ com.fasterxml.jackson com.arangodb.shaded.fasterxml.jackson + + com/arangodb/serde/JacksonSerde.java + com/arangodb/internal/serde/JacksonSerdeImpl.java + com/arangodb/internal/serde/UserJsonMapperProvider.java + com/arangodb/internal/serde/UserMapperProvider.java + com/arangodb/internal/serde/UserVPackMapperProvider.java + + + + com.arangodb.jackson.dataformat.velocypack + com.arangodb.shaded.jackson.dataformat.velocypack io.vertx @@ -99,7 +110,7 @@ com.arangodb:arangodb-java-driver - META-INF/** + META-INF/MANIFEST.MF diff --git a/shaded/src/main/resources/META-INF/vertx/vertx-version.txt b/shaded/src/main/resources/META-INF/vertx/vertx-version.txt deleted file mode 100644 index a6695ff98..000000000 --- a/shaded/src/main/resources/META-INF/vertx/vertx-version.txt +++ /dev/null @@ -1 +0,0 @@ -4.3.4 \ No newline at end of file From 9e4ce83ab7192c1f6bee3a58ca9be05f147c5986 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 6 Dec 2022 15:14:14 +0100 Subject: [PATCH 03/20] serde modules --- driver/pom.xml | 21 ++++++------ .../src/main/java/com/arangodb/ArangoDB.java | 15 +++++--- .../com/arangodb/async/ArangoDBAsync.java | 2 +- .../com/arangodb/entity/DocumentEntity.java | 4 +++ .../internal/InternalArangoDBBuilder.java | 19 ++++++++--- .../internal/http/HttpConnection.java | 3 +- .../internal/serde/ContentTypeFactory.java | 24 +++++++++++++ ...r.java => InternalJsonMapperProvider.java} | 2 +- ...vider.java => InternalMapperProvider.java} | 6 ++-- .../internal/serde/InternalSerde.java | 2 +- ....java => InternalVPackMapperProvider.java} | 2 +- .../com/arangodb/ArangoCollectionTest.java | 3 ++ .../test/java/com/arangodb/BaseJunit5.java | 4 +-- .../arangodb/async/ArangoCollectionTest.java | 1 + .../arangodb/async/example/graph/Circle.java | 6 ++-- .../async/example/graph/CircleEdge.java | 2 +- .../arangodb/async/serde/CustomSerdeTest.java | 3 +- .../com/arangodb/example/graph/Circle.java | 6 ++-- .../arangodb/example/graph/CircleEdge.java | 2 +- .../mapping/annotations/AnnotatedEntity.java | 2 +- .../annotations/ArangoAnnotationsTest.java | 6 ++-- .../com/arangodb/serde/CustomSerdeTest.java | 2 +- .../arangodb/serde/CustomTypeHintTest.java | 1 - .../java/com/arangodb/serde/JsonbSerde.java | 29 ---------------- jackson-serde/pom.xml | 34 +++++++++++++++++++ .../internal/serde/JacksonMapperProvider.java | 6 ++-- .../internal/serde/JacksonSerdeImpl.java | 5 ++- .../serde/JsonJacksonMapperProvider.java | 2 +- .../serde/VPackJacksonMapperProvider.java | 2 +- .../main/java/com/arangodb/serde}/From.java | 9 ++--- .../src/main/java/com/arangodb/serde}/Id.java | 9 ++--- .../java/com/arangodb/serde/JacksonSerde.java | 19 +++++++++++ .../arangodb/serde/JacksonSerdeProvider.java | 21 +++--------- .../main/java/com/arangodb/serde}/Key.java | 9 ++--- .../main/java/com/arangodb/serde}/Rev.java | 9 ++--- .../src/main/java/com/arangodb/serde}/To.java | 9 ++--- .../com.arangodb.serde.ArangoSerdeProvider | 1 + jsonb-serde/pom.xml | 33 ++++++++++++++++++ .../main/java/com/arangodb/serde/From.java | 19 +++++++++++ .../src/main/java/com/arangodb/serde/Id.java | 19 +++++++++++ .../java/com/arangodb/serde/JsonbSerde.java | 9 +++-- .../arangodb/serde/JsonbSerdeProvider.java | 32 +++++++++++++++++ .../src/main/java/com/arangodb/serde/Key.java | 19 +++++++++++ .../src/main/java/com/arangodb/serde/Rev.java | 19 +++++++++++ .../src/main/java/com/arangodb/serde/To.java | 19 +++++++++++ .../com.arangodb.serde.ArangoSerdeProvider | 1 + pom.xml | 3 ++ serde-api/pom.xml | 20 +++++++++++ .../main/java/com/arangodb/ContentType.java | 16 +-------- .../java/com/arangodb/serde/ArangoSerde.java | 6 ---- .../arangodb/serde/ArangoSerdeProvider.java | 15 ++++++++ shaded/pom.xml | 27 ++++----------- 52 files changed, 381 insertions(+), 178 deletions(-) create mode 100644 driver/src/main/java/com/arangodb/internal/serde/ContentTypeFactory.java rename driver/src/main/java/com/arangodb/internal/serde/{JsonMapperProvider.java => InternalJsonMapperProvider.java} (74%) rename driver/src/main/java/com/arangodb/internal/serde/{MapperProvider.java => InternalMapperProvider.java} (69%) rename driver/src/main/java/com/arangodb/internal/serde/{VPackMapperProvider.java => InternalVPackMapperProvider.java} (78%) delete mode 100644 driver/src/test/java/com/arangodb/serde/JsonbSerde.java create mode 100644 jackson-serde/pom.xml rename driver/src/main/java/com/arangodb/internal/serde/UserMapperProvider.java => jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonMapperProvider.java (71%) rename {driver => jackson-serde}/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java (92%) rename driver/src/main/java/com/arangodb/internal/serde/UserJsonMapperProvider.java => jackson-serde/src/main/java/com/arangodb/internal/serde/JsonJacksonMapperProvider.java (79%) rename driver/src/main/java/com/arangodb/internal/serde/UserVPackMapperProvider.java => jackson-serde/src/main/java/com/arangodb/internal/serde/VPackJacksonMapperProvider.java (82%) rename {driver/src/main/java/com/arangodb/entity => jackson-serde/src/main/java/com/arangodb/serde}/From.java (68%) rename {driver/src/main/java/com/arangodb/entity => jackson-serde/src/main/java/com/arangodb/serde}/Id.java (68%) create mode 100644 jackson-serde/src/main/java/com/arangodb/serde/JacksonSerde.java rename driver/src/main/java/com/arangodb/serde/JacksonSerde.java => jackson-serde/src/main/java/com/arangodb/serde/JacksonSerdeProvider.java (53%) rename {driver/src/main/java/com/arangodb/entity => jackson-serde/src/main/java/com/arangodb/serde}/Key.java (68%) rename {driver/src/main/java/com/arangodb/entity => jackson-serde/src/main/java/com/arangodb/serde}/Rev.java (68%) rename {driver/src/main/java/com/arangodb/entity => jackson-serde/src/main/java/com/arangodb/serde}/To.java (68%) create mode 100644 jackson-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider create mode 100644 jsonb-serde/pom.xml create mode 100644 jsonb-serde/src/main/java/com/arangodb/serde/From.java create mode 100644 jsonb-serde/src/main/java/com/arangodb/serde/Id.java rename driver/src/test/java/com/arangodb/serde/JsonbSerdeImpl.java => jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerde.java (76%) create mode 100644 jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerdeProvider.java create mode 100644 jsonb-serde/src/main/java/com/arangodb/serde/Key.java create mode 100644 jsonb-serde/src/main/java/com/arangodb/serde/Rev.java create mode 100644 jsonb-serde/src/main/java/com/arangodb/serde/To.java create mode 100644 jsonb-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider create mode 100644 serde-api/pom.xml rename {driver => serde-api}/src/main/java/com/arangodb/ContentType.java (62%) rename {driver => serde-api}/src/main/java/com/arangodb/serde/ArangoSerde.java (84%) create mode 100644 serde-api/src/main/java/com/arangodb/serde/ArangoSerdeProvider.java diff --git a/driver/pom.xml b/driver/pom.xml index c372e221d..d31a0afc2 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -334,6 +334,16 @@ + + com.arangodb + serde-api + ${project.version} + + + com.arangodb + jackson-serde + ${project.version} + org.slf4j slf4j-api @@ -361,11 +371,6 @@ 3.1.0 true - - jakarta.json.bind - jakarta.json.bind-api - 2.0.0 - com.google.code.findbugs jsr305 @@ -411,12 +416,6 @@ 22.2.0 test - - org.eclipse - yasson - 2.0.4 - test - ru.lanwen.verbalregex java-verbal-expressions diff --git a/driver/src/main/java/com/arangodb/ArangoDB.java b/driver/src/main/java/com/arangodb/ArangoDB.java index cc371bbe8..956583e0a 100644 --- a/driver/src/main/java/com/arangodb/ArangoDB.java +++ b/driver/src/main/java/com/arangodb/ArangoDB.java @@ -23,10 +23,16 @@ import com.arangodb.config.ConfigPropertiesProvider; import com.arangodb.config.ConfigPropertyKey; import com.arangodb.entity.*; -import com.arangodb.internal.*; +import com.arangodb.internal.ArangoDBImpl; +import com.arangodb.internal.ArangoDefaults; +import com.arangodb.internal.InternalArangoDBBuilder; import com.arangodb.internal.http.HttpCommunication; import com.arangodb.internal.http.HttpConnectionFactory; -import com.arangodb.internal.net.*; +import com.arangodb.internal.net.ConnectionFactory; +import com.arangodb.internal.net.Host; +import com.arangodb.internal.net.HostHandler; +import com.arangodb.internal.net.HostResolver; +import com.arangodb.internal.serde.ContentTypeFactory; import com.arangodb.internal.serde.InternalSerde; import com.arangodb.internal.velocystream.VstCommunicationSync; import com.arangodb.internal.velocystream.VstConnectionFactorySync; @@ -35,7 +41,6 @@ import com.arangodb.model.UserCreateOptions; import com.arangodb.model.UserUpdateOptions; import com.arangodb.serde.ArangoSerde; -import com.arangodb.serde.JacksonSerde; import javax.annotation.concurrent.ThreadSafe; import javax.net.ssl.SSLContext; @@ -581,8 +586,8 @@ public ArangoDB build() { } final ArangoSerde userSerde = this.userDataSerde != null ? this.userDataSerde : - JacksonSerde.of(ContentType.of(protocol)); - final InternalSerde serde = InternalSerde.of(ContentType.of(protocol), userSerde); + serdeProvider().of(ContentTypeFactory.of(protocol)); + final InternalSerde serde = InternalSerde.of(ContentTypeFactory.of(protocol), userSerde); int protocolMaxConnections; switch (protocol) { diff --git a/driver/src/main/java/com/arangodb/async/ArangoDBAsync.java b/driver/src/main/java/com/arangodb/async/ArangoDBAsync.java index be0209d8c..64581be1c 100644 --- a/driver/src/main/java/com/arangodb/async/ArangoDBAsync.java +++ b/driver/src/main/java/com/arangodb/async/ArangoDBAsync.java @@ -516,7 +516,7 @@ public ArangoDBAsync build() { throw new ArangoDBException("No host has been set!"); } - final ArangoSerde userSerde = this.userDataSerde != null ? this.userDataSerde : JacksonSerde.of(ContentType.VPACK); + final ArangoSerde userSerde = this.userDataSerde != null ? this.userDataSerde : serdeProvider().of(ContentType.VPACK); final InternalSerde serde = InternalSerde.of(ContentType.VPACK, userSerde); final int max = maxConnections != null ? Math.max(1, maxConnections) diff --git a/driver/src/main/java/com/arangodb/entity/DocumentEntity.java b/driver/src/main/java/com/arangodb/entity/DocumentEntity.java index fe3ddda5b..af706faa6 100644 --- a/driver/src/main/java/com/arangodb/entity/DocumentEntity.java +++ b/driver/src/main/java/com/arangodb/entity/DocumentEntity.java @@ -20,6 +20,10 @@ package com.arangodb.entity; +import com.arangodb.serde.Id; +import com.arangodb.serde.Key; +import com.arangodb.serde.Rev; + /** * @author Mark Vollmary */ diff --git a/driver/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java b/driver/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java index df0d49496..e493a6207 100644 --- a/driver/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java +++ b/driver/src/main/java/com/arangodb/internal/InternalArangoDBBuilder.java @@ -27,14 +27,12 @@ import com.arangodb.internal.net.*; import com.arangodb.internal.util.HostUtils; import com.arangodb.serde.ArangoSerde; +import com.arangodb.serde.ArangoSerdeProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLContext; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Locale; +import java.util.*; /** @@ -144,6 +142,19 @@ protected static String getProperty(ConfigPropertiesProvider props, ConfigProper return props.getProperty(key, defaultValue); } + protected static ArangoSerdeProvider serdeProvider() { + ServiceLoader loader = ServiceLoader.load(ArangoSerdeProvider.class); + Iterator it = loader.iterator(); + if (!it.hasNext()) { + throw new ArangoDBException("No ArangoSerdeProvider found!"); + } + ArangoSerdeProvider serdeProvider = it.next(); + if (it.hasNext()) { + throw new ArangoDBException("Found multiple serde providers! Please set explicitly the one to use."); + } + return serdeProvider; + } + protected void doLoadProperties(final ConfigPropertiesProvider properties) { loadHosts(properties, hosts); timeout = loadTimeout(properties, timeout); diff --git a/driver/src/main/java/com/arangodb/internal/http/HttpConnection.java b/driver/src/main/java/com/arangodb/internal/http/HttpConnection.java index a62ef669e..e031c636d 100644 --- a/driver/src/main/java/com/arangodb/internal/http/HttpConnection.java +++ b/driver/src/main/java/com/arangodb/internal/http/HttpConnection.java @@ -23,6 +23,7 @@ import com.arangodb.*; import com.arangodb.internal.net.Connection; import com.arangodb.internal.net.HostDescription; +import com.arangodb.internal.serde.ContentTypeFactory; import com.arangodb.internal.serde.InternalSerde; import com.arangodb.internal.util.EncodeUtils; import com.arangodb.internal.util.ResponseUtils; @@ -84,7 +85,7 @@ private HttpConnection(final HostDescription host, final Integer timeout, final final InternalSerde util, final Protocol protocol, final Long ttl) { super(); this.util = util; - this.contentType = ContentType.of(protocol); + this.contentType = ContentTypeFactory.of(protocol); this.timeout = timeout; vertx = Vertx.vertx(new VertxOptions().setPreferNativeTransport(true).setEventLoopPoolSize(1)); vertx.runOnContext(e -> { diff --git a/driver/src/main/java/com/arangodb/internal/serde/ContentTypeFactory.java b/driver/src/main/java/com/arangodb/internal/serde/ContentTypeFactory.java new file mode 100644 index 000000000..453cc2a0f --- /dev/null +++ b/driver/src/main/java/com/arangodb/internal/serde/ContentTypeFactory.java @@ -0,0 +1,24 @@ +package com.arangodb.internal.serde; + +import com.arangodb.ContentType; +import com.arangodb.Protocol; + +public final class ContentTypeFactory { + private ContentTypeFactory() { + } + + public static ContentType of(Protocol protocol) { + switch (protocol) { + case HTTP_JSON: + case HTTP2_JSON: + return ContentType.JSON; + case VST: + case HTTP_VPACK: + case HTTP2_VPACK: + return ContentType.VPACK; + default: + throw new IllegalArgumentException("Unexpected value: " + protocol); + } + } + +} diff --git a/driver/src/main/java/com/arangodb/internal/serde/JsonMapperProvider.java b/driver/src/main/java/com/arangodb/internal/serde/InternalJsonMapperProvider.java similarity index 74% rename from driver/src/main/java/com/arangodb/internal/serde/JsonMapperProvider.java rename to driver/src/main/java/com/arangodb/internal/serde/InternalJsonMapperProvider.java index e3ba08eb5..343c387d6 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/JsonMapperProvider.java +++ b/driver/src/main/java/com/arangodb/internal/serde/InternalJsonMapperProvider.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; -enum JsonMapperProvider implements MapperProvider { +enum InternalJsonMapperProvider implements InternalMapperProvider { INSTANCE; @Override diff --git a/driver/src/main/java/com/arangodb/internal/serde/MapperProvider.java b/driver/src/main/java/com/arangodb/internal/serde/InternalMapperProvider.java similarity index 69% rename from driver/src/main/java/com/arangodb/internal/serde/MapperProvider.java rename to driver/src/main/java/com/arangodb/internal/serde/InternalMapperProvider.java index a4431a6cd..fca65bd24 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/MapperProvider.java +++ b/driver/src/main/java/com/arangodb/internal/serde/InternalMapperProvider.java @@ -5,12 +5,12 @@ import java.util.function.Supplier; -public interface MapperProvider extends Supplier { +public interface InternalMapperProvider extends Supplier { static ObjectMapper of(final ContentType contentType) { if (contentType == ContentType.JSON) { - return JsonMapperProvider.INSTANCE.get(); + return InternalJsonMapperProvider.INSTANCE.get(); } else if (contentType == ContentType.VPACK) { - return VPackMapperProvider.INSTANCE.get(); + return InternalVPackMapperProvider.INSTANCE.get(); } else { throw new IllegalArgumentException("Unexpected value: " + contentType); } diff --git a/driver/src/main/java/com/arangodb/internal/serde/InternalSerde.java b/driver/src/main/java/com/arangodb/internal/serde/InternalSerde.java index 2d0543093..aeca0e880 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/InternalSerde.java +++ b/driver/src/main/java/com/arangodb/internal/serde/InternalSerde.java @@ -16,7 +16,7 @@ public interface InternalSerde extends ArangoSerde { * @return the created InternalSerde */ static InternalSerde of(final ContentType contentType, ArangoSerde userSerde) { - return new InternalSerdeImpl(MapperProvider.of(contentType), userSerde); + return new InternalSerdeImpl(InternalMapperProvider.of(contentType), userSerde); } /** diff --git a/driver/src/main/java/com/arangodb/internal/serde/VPackMapperProvider.java b/driver/src/main/java/com/arangodb/internal/serde/InternalVPackMapperProvider.java similarity index 78% rename from driver/src/main/java/com/arangodb/internal/serde/VPackMapperProvider.java rename to driver/src/main/java/com/arangodb/internal/serde/InternalVPackMapperProvider.java index 0762414fc..e157b3dc2 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/VPackMapperProvider.java +++ b/driver/src/main/java/com/arangodb/internal/serde/InternalVPackMapperProvider.java @@ -3,7 +3,7 @@ import com.arangodb.jackson.dataformat.velocypack.VPackMapper; import com.fasterxml.jackson.databind.ObjectMapper; -enum VPackMapperProvider implements MapperProvider { +enum InternalVPackMapperProvider implements InternalMapperProvider { INSTANCE; @Override diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionTest.java index 66c34ba34..3dbc25012 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionTest.java @@ -24,7 +24,10 @@ import com.arangodb.internal.serde.SerdeUtils; import com.arangodb.model.*; import com.arangodb.model.DocumentImportOptions.OnDuplicate; +import com.arangodb.serde.Id; import com.arangodb.serde.JacksonSerde; +import com.arangodb.serde.Key; +import com.arangodb.serde.Rev; import com.arangodb.util.MapBuilder; import com.arangodb.util.RawBytes; import com.arangodb.util.RawData; diff --git a/driver/src/test/java/com/arangodb/BaseJunit5.java b/driver/src/test/java/com/arangodb/BaseJunit5.java index f88cac877..ed1813514 100644 --- a/driver/src/test/java/com/arangodb/BaseJunit5.java +++ b/driver/src/test/java/com/arangodb/BaseJunit5.java @@ -4,7 +4,6 @@ import com.arangodb.internal.config.FileConfigPropertiesProvider; import com.arangodb.model.CollectionCreateOptions; import com.arangodb.model.GraphCreateOptions; -import com.arangodb.serde.JsonbSerde; import com.arangodb.util.TestUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -24,8 +23,7 @@ class BaseJunit5 { new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP_VPACK).build(), new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP_JSON).build(), new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP2_VPACK).build(), - new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP2_JSON).build(), - new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP2_JSON).serde(JsonbSerde.create()).build() + new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP2_JSON).build() ); protected static Stream dbsStream() { diff --git a/driver/src/test/java/com/arangodb/async/ArangoCollectionTest.java b/driver/src/test/java/com/arangodb/async/ArangoCollectionTest.java index 69090d7c8..246660203 100644 --- a/driver/src/test/java/com/arangodb/async/ArangoCollectionTest.java +++ b/driver/src/test/java/com/arangodb/async/ArangoCollectionTest.java @@ -26,6 +26,7 @@ import com.arangodb.model.*; import com.arangodb.model.DocumentImportOptions.OnDuplicate; import com.arangodb.serde.JacksonSerde; +import com.arangodb.serde.Key; import com.arangodb.util.RawData; import com.arangodb.util.RawJson; import com.fasterxml.jackson.annotation.JsonTypeInfo; diff --git a/driver/src/test/java/com/arangodb/async/example/graph/Circle.java b/driver/src/test/java/com/arangodb/async/example/graph/Circle.java index 4df7cfcc1..59573e539 100644 --- a/driver/src/test/java/com/arangodb/async/example/graph/Circle.java +++ b/driver/src/test/java/com/arangodb/async/example/graph/Circle.java @@ -20,9 +20,9 @@ package com.arangodb.async.example.graph; -import com.arangodb.entity.Id; -import com.arangodb.entity.Key; -import com.arangodb.entity.Rev; +import com.arangodb.serde.Id; +import com.arangodb.serde.Key; +import com.arangodb.serde.Rev; /** * @author a-brandt diff --git a/driver/src/test/java/com/arangodb/async/example/graph/CircleEdge.java b/driver/src/test/java/com/arangodb/async/example/graph/CircleEdge.java index aa89d8d35..30fd2ef39 100644 --- a/driver/src/test/java/com/arangodb/async/example/graph/CircleEdge.java +++ b/driver/src/test/java/com/arangodb/async/example/graph/CircleEdge.java @@ -20,7 +20,7 @@ package com.arangodb.async.example.graph; -import com.arangodb.entity.*; +import com.arangodb.serde.*; /** * @author a-brandt diff --git a/driver/src/test/java/com/arangodb/async/serde/CustomSerdeTest.java b/driver/src/test/java/com/arangodb/async/serde/CustomSerdeTest.java index 1b8e71a97..91ccc0add 100644 --- a/driver/src/test/java/com/arangodb/async/serde/CustomSerdeTest.java +++ b/driver/src/test/java/com/arangodb/async/serde/CustomSerdeTest.java @@ -29,6 +29,7 @@ import com.arangodb.model.DocumentCreateOptions; import com.arangodb.ContentType; import com.arangodb.serde.JacksonSerde; +import com.arangodb.serde.JacksonSerdeProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -57,7 +58,7 @@ class CustomSerdeTest { @BeforeEach void init() throws ExecutionException, InterruptedException { - JacksonSerde serde = JacksonSerde.of(ContentType.VPACK); + JacksonSerde serde = new JacksonSerdeProvider().of(ContentType.VPACK); serde.configure((mapper) -> { mapper.configure(WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true); mapper.configure(USE_BIG_INTEGER_FOR_INTS, true); diff --git a/driver/src/test/java/com/arangodb/example/graph/Circle.java b/driver/src/test/java/com/arangodb/example/graph/Circle.java index 8e1a00431..88e765631 100644 --- a/driver/src/test/java/com/arangodb/example/graph/Circle.java +++ b/driver/src/test/java/com/arangodb/example/graph/Circle.java @@ -20,9 +20,9 @@ package com.arangodb.example.graph; -import com.arangodb.entity.Id; -import com.arangodb.entity.Key; -import com.arangodb.entity.Rev; +import com.arangodb.serde.Id; +import com.arangodb.serde.Key; +import com.arangodb.serde.Rev; /** * @author a-brandt diff --git a/driver/src/test/java/com/arangodb/example/graph/CircleEdge.java b/driver/src/test/java/com/arangodb/example/graph/CircleEdge.java index e55e3bc1b..15508ff33 100644 --- a/driver/src/test/java/com/arangodb/example/graph/CircleEdge.java +++ b/driver/src/test/java/com/arangodb/example/graph/CircleEdge.java @@ -20,7 +20,7 @@ package com.arangodb.example.graph; -import com.arangodb.entity.*; +import com.arangodb.serde.*; /** * @author a-brandt diff --git a/driver/src/test/java/com/arangodb/mapping/annotations/AnnotatedEntity.java b/driver/src/test/java/com/arangodb/mapping/annotations/AnnotatedEntity.java index e3ce6ea78..26f6a208c 100644 --- a/driver/src/test/java/com/arangodb/mapping/annotations/AnnotatedEntity.java +++ b/driver/src/test/java/com/arangodb/mapping/annotations/AnnotatedEntity.java @@ -1,6 +1,6 @@ package com.arangodb.mapping.annotations; -import com.arangodb.entity.*; +import com.arangodb.serde.*; import java.util.Objects; diff --git a/driver/src/test/java/com/arangodb/mapping/annotations/ArangoAnnotationsTest.java b/driver/src/test/java/com/arangodb/mapping/annotations/ArangoAnnotationsTest.java index a2ca82f22..5bdf72474 100644 --- a/driver/src/test/java/com/arangodb/mapping/annotations/ArangoAnnotationsTest.java +++ b/driver/src/test/java/com/arangodb/mapping/annotations/ArangoAnnotationsTest.java @@ -20,9 +20,9 @@ package com.arangodb.mapping.annotations; -import com.arangodb.serde.ArangoSerde; import com.arangodb.ContentType; -import com.arangodb.serde.JacksonSerde; +import com.arangodb.serde.ArangoSerde; +import com.arangodb.serde.JacksonSerdeProvider; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -38,7 +38,7 @@ class ArangoAnnotationsTest { @ParameterizedTest @EnumSource(ContentType.class) void documentFieldAnnotations(ContentType contentType) { - ArangoSerde mapper = JacksonSerde.of(contentType); + ArangoSerde mapper = new JacksonSerdeProvider().of(contentType); AnnotatedEntity e = new AnnotatedEntity(); e.setId("Id"); diff --git a/driver/src/test/java/com/arangodb/serde/CustomSerdeTest.java b/driver/src/test/java/com/arangodb/serde/CustomSerdeTest.java index a1d75fc39..a12cbb796 100644 --- a/driver/src/test/java/com/arangodb/serde/CustomSerdeTest.java +++ b/driver/src/test/java/com/arangodb/serde/CustomSerdeTest.java @@ -61,7 +61,7 @@ class CustomSerdeTest { @BeforeAll static void init() { - JacksonSerde serde = JacksonSerde.of(ContentType.VPACK); + JacksonSerde serde = new JacksonSerdeProvider().of(ContentType.VPACK); serde.configure((mapper) -> { mapper.configure(WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true); mapper.configure(USE_BIG_INTEGER_FOR_INTS, true); diff --git a/driver/src/test/java/com/arangodb/serde/CustomTypeHintTest.java b/driver/src/test/java/com/arangodb/serde/CustomTypeHintTest.java index 2de790a5e..d0a9cb8ae 100644 --- a/driver/src/test/java/com/arangodb/serde/CustomTypeHintTest.java +++ b/driver/src/test/java/com/arangodb/serde/CustomTypeHintTest.java @@ -25,7 +25,6 @@ import com.arangodb.ArangoDB; import com.arangodb.ArangoDatabase; import com.arangodb.DbName; -import com.arangodb.entity.Key; import com.arangodb.internal.config.FileConfigPropertiesProvider; import com.arangodb.model.DocumentCreateOptions; import com.fasterxml.jackson.annotation.JsonTypeInfo; diff --git a/driver/src/test/java/com/arangodb/serde/JsonbSerde.java b/driver/src/test/java/com/arangodb/serde/JsonbSerde.java deleted file mode 100644 index c6dac0dcc..000000000 --- a/driver/src/test/java/com/arangodb/serde/JsonbSerde.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.arangodb.serde; - -import jakarta.json.bind.JsonbConfig; - -/** - * User data serde based on Jakarta JSON Binding (JSON-B). - */ -public interface JsonbSerde extends ArangoSerde { - - /** - * Creates a new JsonbSerde with default settings. - * - * @return the created JsonbSerde - */ - static JsonbSerde create() { - return new JsonbSerdeImpl(); - } - - /** - * Creates a new JsonbSerde using the provided . - * - * @param config JsonbConfig to use - * @return the created JsonbSerde - */ - static JsonbSerde of(final JsonbConfig config) { - return new JsonbSerdeImpl(config); - } - -} diff --git a/jackson-serde/pom.xml b/jackson-serde/pom.xml new file mode 100644 index 000000000..9ad57cc61 --- /dev/null +++ b/jackson-serde/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + com.arangodb + arangodb-java-driver-parent + 7.0.0-SNAPSHOT + + + jackson-serde + + + 1.8 + 1.8 + UTF-8 + + + + + com.arangodb + jackson-dataformat-velocypack + 3.1.0 + provided + + + com.arangodb + serde-api + ${project.version} + provided + + + \ No newline at end of file diff --git a/driver/src/main/java/com/arangodb/internal/serde/UserMapperProvider.java b/jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonMapperProvider.java similarity index 71% rename from driver/src/main/java/com/arangodb/internal/serde/UserMapperProvider.java rename to jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonMapperProvider.java index 3fe781ca9..e31a79503 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/UserMapperProvider.java +++ b/jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonMapperProvider.java @@ -8,12 +8,12 @@ /** * Not shaded in arangodb-java-driver-shaded. */ -public interface UserMapperProvider extends Supplier { +public interface JacksonMapperProvider extends Supplier { static ObjectMapper of(final ContentType contentType) { if (contentType == ContentType.JSON) { - return UserJsonMapperProvider.INSTANCE.get(); + return JsonJacksonMapperProvider.INSTANCE.get(); } else if (contentType == ContentType.VPACK) { - return UserVPackMapperProvider.INSTANCE.get(); + return VPackJacksonMapperProvider.INSTANCE.get(); } else { throw new IllegalArgumentException("Unexpected value: " + contentType); } diff --git a/driver/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java b/jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java similarity index 92% rename from driver/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java rename to jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java index 1542929e7..e870f7413 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java +++ b/jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java @@ -1,6 +1,5 @@ package com.arangodb.internal.serde; -import com.arangodb.ArangoDBException; import com.arangodb.serde.JacksonSerde; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -33,7 +32,7 @@ public byte[] serialize(final Object value) { try { return mapper.writeValueAsBytes(value); } catch (JsonProcessingException e) { - throw new ArangoDBException(e); + throw new RuntimeException(e); } } @@ -42,7 +41,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 new RuntimeException(e); } } diff --git a/driver/src/main/java/com/arangodb/internal/serde/UserJsonMapperProvider.java b/jackson-serde/src/main/java/com/arangodb/internal/serde/JsonJacksonMapperProvider.java similarity index 79% rename from driver/src/main/java/com/arangodb/internal/serde/UserJsonMapperProvider.java rename to jackson-serde/src/main/java/com/arangodb/internal/serde/JsonJacksonMapperProvider.java index 4ff51de04..cc6ec3a3a 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/UserJsonMapperProvider.java +++ b/jackson-serde/src/main/java/com/arangodb/internal/serde/JsonJacksonMapperProvider.java @@ -5,7 +5,7 @@ /** * Not shaded in arangodb-java-driver-shaded. */ -enum UserJsonMapperProvider implements MapperProvider { +enum JsonJacksonMapperProvider implements JacksonMapperProvider { INSTANCE; @Override diff --git a/driver/src/main/java/com/arangodb/internal/serde/UserVPackMapperProvider.java b/jackson-serde/src/main/java/com/arangodb/internal/serde/VPackJacksonMapperProvider.java similarity index 82% rename from driver/src/main/java/com/arangodb/internal/serde/UserVPackMapperProvider.java rename to jackson-serde/src/main/java/com/arangodb/internal/serde/VPackJacksonMapperProvider.java index 64115cd96..aa0fda5fa 100644 --- a/driver/src/main/java/com/arangodb/internal/serde/UserVPackMapperProvider.java +++ b/jackson-serde/src/main/java/com/arangodb/internal/serde/VPackJacksonMapperProvider.java @@ -6,7 +6,7 @@ /** * Not shaded in arangodb-java-driver-shaded. */ -enum UserVPackMapperProvider implements MapperProvider { +enum VPackJacksonMapperProvider implements JacksonMapperProvider { INSTANCE; @Override diff --git a/driver/src/main/java/com/arangodb/entity/From.java b/jackson-serde/src/main/java/com/arangodb/serde/From.java similarity index 68% rename from driver/src/main/java/com/arangodb/entity/From.java rename to jackson-serde/src/main/java/com/arangodb/serde/From.java index 1e82ae341..91b85bdb1 100644 --- a/driver/src/main/java/com/arangodb/entity/From.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/From.java @@ -1,11 +1,8 @@ -package com.arangodb.entity; +package com.arangodb.serde; -import com.arangodb.internal.DocumentFields; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.json.bind.annotation.JsonbAnnotation; -import jakarta.json.bind.annotation.JsonbProperty; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -18,9 +15,7 @@ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside -@JsonProperty(DocumentFields.FROM) +@JsonProperty("_from") @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonbAnnotation -@JsonbProperty(DocumentFields.FROM) public @interface From { } diff --git a/driver/src/main/java/com/arangodb/entity/Id.java b/jackson-serde/src/main/java/com/arangodb/serde/Id.java similarity index 68% rename from driver/src/main/java/com/arangodb/entity/Id.java rename to jackson-serde/src/main/java/com/arangodb/serde/Id.java index f0e55dcb6..0132d7fad 100644 --- a/driver/src/main/java/com/arangodb/entity/Id.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/Id.java @@ -1,11 +1,8 @@ -package com.arangodb.entity; +package com.arangodb.serde; -import com.arangodb.internal.DocumentFields; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.json.bind.annotation.JsonbAnnotation; -import jakarta.json.bind.annotation.JsonbProperty; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -18,9 +15,7 @@ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside -@JsonProperty(DocumentFields.ID) +@JsonProperty("_id") @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonbAnnotation -@JsonbProperty(DocumentFields.ID) public @interface Id { } diff --git a/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerde.java b/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerde.java new file mode 100644 index 000000000..888740587 --- /dev/null +++ b/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerde.java @@ -0,0 +1,19 @@ +package com.arangodb.serde; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.function.Consumer; + +/** + * User data serde based on Jackson Databind. Not shaded in arangodb-java-driver-shaded. + */ +public interface JacksonSerde extends ArangoSerde { + + /** + * Allows configuring the underlying Jackson ObjectMapper + * + * @param configureFunction function to configure the Jackson ObjectMapper + */ + void configure(final Consumer configureFunction); + +} diff --git a/driver/src/main/java/com/arangodb/serde/JacksonSerde.java b/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerdeProvider.java similarity index 53% rename from driver/src/main/java/com/arangodb/serde/JacksonSerde.java rename to jackson-serde/src/main/java/com/arangodb/serde/JacksonSerdeProvider.java index 8125e5910..005060f17 100644 --- a/driver/src/main/java/com/arangodb/serde/JacksonSerde.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerdeProvider.java @@ -2,15 +2,10 @@ import com.arangodb.ContentType; import com.arangodb.internal.serde.JacksonSerdeImpl; -import com.arangodb.internal.serde.UserMapperProvider; +import com.arangodb.internal.serde.JacksonMapperProvider; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.function.Consumer; - -/** - * User data serde based on Jackson Databind. Not shaded in arangodb-java-driver-shaded. - */ -public interface JacksonSerde extends ArangoSerde { +public class JacksonSerdeProvider implements ArangoSerdeProvider { /** * Creates a new JacksonSerde with default settings for the specified data type. @@ -18,8 +13,9 @@ public interface JacksonSerde extends ArangoSerde { * @param contentType serialization target data type * @return the created JacksonSerde */ - static JacksonSerde of(final ContentType contentType) { - return create(UserMapperProvider.of(contentType)); + @Override + public JacksonSerde of(final ContentType contentType) { + return create(JacksonMapperProvider.of(contentType)); } /** @@ -32,11 +28,4 @@ static JacksonSerde create(final ObjectMapper mapper) { return new JacksonSerdeImpl(mapper); } - /** - * Allows configuring the underlying Jackson ObjectMapper - * - * @param configureFunction function to configure the Jackson ObjectMapper - */ - void configure(final Consumer configureFunction); - } diff --git a/driver/src/main/java/com/arangodb/entity/Key.java b/jackson-serde/src/main/java/com/arangodb/serde/Key.java similarity index 68% rename from driver/src/main/java/com/arangodb/entity/Key.java rename to jackson-serde/src/main/java/com/arangodb/serde/Key.java index fd146d011..519e1af4c 100644 --- a/driver/src/main/java/com/arangodb/entity/Key.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/Key.java @@ -1,11 +1,8 @@ -package com.arangodb.entity; +package com.arangodb.serde; -import com.arangodb.internal.DocumentFields; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.json.bind.annotation.JsonbAnnotation; -import jakarta.json.bind.annotation.JsonbProperty; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -18,9 +15,7 @@ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside -@JsonProperty(DocumentFields.KEY) +@JsonProperty("_key") @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonbAnnotation -@JsonbProperty(DocumentFields.KEY) public @interface Key { } diff --git a/driver/src/main/java/com/arangodb/entity/Rev.java b/jackson-serde/src/main/java/com/arangodb/serde/Rev.java similarity index 68% rename from driver/src/main/java/com/arangodb/entity/Rev.java rename to jackson-serde/src/main/java/com/arangodb/serde/Rev.java index 63189e565..3915881a7 100644 --- a/driver/src/main/java/com/arangodb/entity/Rev.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/Rev.java @@ -1,11 +1,8 @@ -package com.arangodb.entity; +package com.arangodb.serde; -import com.arangodb.internal.DocumentFields; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.json.bind.annotation.JsonbAnnotation; -import jakarta.json.bind.annotation.JsonbProperty; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -18,9 +15,7 @@ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside -@JsonProperty(DocumentFields.REV) +@JsonProperty("_rev") @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonbAnnotation -@JsonbProperty(DocumentFields.REV) public @interface Rev { } diff --git a/driver/src/main/java/com/arangodb/entity/To.java b/jackson-serde/src/main/java/com/arangodb/serde/To.java similarity index 68% rename from driver/src/main/java/com/arangodb/entity/To.java rename to jackson-serde/src/main/java/com/arangodb/serde/To.java index 113496d86..64a1f2581 100644 --- a/driver/src/main/java/com/arangodb/entity/To.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/To.java @@ -1,11 +1,8 @@ -package com.arangodb.entity; +package com.arangodb.serde; -import com.arangodb.internal.DocumentFields; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.json.bind.annotation.JsonbAnnotation; -import jakarta.json.bind.annotation.JsonbProperty; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -18,9 +15,7 @@ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside -@JsonProperty(DocumentFields.TO) +@JsonProperty("_to") @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonbAnnotation -@JsonbProperty(DocumentFields.TO) public @interface To { } diff --git a/jackson-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider b/jackson-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider new file mode 100644 index 000000000..88eb1d8bd --- /dev/null +++ b/jackson-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider @@ -0,0 +1 @@ +com.arangodb.serde.JacksonSerdeProvider diff --git a/jsonb-serde/pom.xml b/jsonb-serde/pom.xml new file mode 100644 index 000000000..aa32fda65 --- /dev/null +++ b/jsonb-serde/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + com.arangodb + arangodb-java-driver-parent + 7.0.0-SNAPSHOT + + + jsonb-serde + + + 1.8 + 1.8 + UTF-8 + + + + + com.arangodb + serde-api + ${project.version} + + + jakarta.json.bind + jakarta.json.bind-api + 3.0.0 + + + + \ No newline at end of file diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/From.java b/jsonb-serde/src/main/java/com/arangodb/serde/From.java new file mode 100644 index 000000000..31b828dd0 --- /dev/null +++ b/jsonb-serde/src/main/java/com/arangodb/serde/From.java @@ -0,0 +1,19 @@ +package com.arangodb.serde; + +import jakarta.json.bind.annotation.JsonbAnnotation; +import jakarta.json.bind.annotation.JsonbProperty; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Michele Rastelli + */ +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@JsonbAnnotation +@JsonbProperty("_from") +public @interface From { +} diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/Id.java b/jsonb-serde/src/main/java/com/arangodb/serde/Id.java new file mode 100644 index 000000000..52ef9b21d --- /dev/null +++ b/jsonb-serde/src/main/java/com/arangodb/serde/Id.java @@ -0,0 +1,19 @@ +package com.arangodb.serde; + +import jakarta.json.bind.annotation.JsonbAnnotation; +import jakarta.json.bind.annotation.JsonbProperty; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Michele Rastelli + */ +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@JsonbAnnotation +@JsonbProperty("_id") +public @interface Id { +} diff --git a/driver/src/test/java/com/arangodb/serde/JsonbSerdeImpl.java b/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerde.java similarity index 76% rename from driver/src/test/java/com/arangodb/serde/JsonbSerdeImpl.java rename to jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerde.java index a19dacbbf..0bc6a2f2d 100644 --- a/driver/src/test/java/com/arangodb/serde/JsonbSerdeImpl.java +++ b/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerde.java @@ -7,15 +7,18 @@ import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; -public class JsonbSerdeImpl implements JsonbSerde { +/** + * User data serde based on Jakarta JSON Binding (JSON-B). + */ +public class JsonbSerde implements ArangoSerde { private final Jsonb jsonb; - public JsonbSerdeImpl() { + public JsonbSerde() { jsonb = JsonbBuilder.create(); } - public JsonbSerdeImpl(final JsonbConfig config) { + public JsonbSerde(final JsonbConfig config) { jsonb = JsonbBuilder.create(config); } diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerdeProvider.java b/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerdeProvider.java new file mode 100644 index 000000000..8aaa56c2e --- /dev/null +++ b/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerdeProvider.java @@ -0,0 +1,32 @@ +package com.arangodb.serde; + +import com.arangodb.ContentType; +import jakarta.json.bind.JsonbConfig; + +import java.util.Objects; + +public class JsonbSerdeProvider implements ArangoSerdeProvider { + + /** + * Creates a new JsonbSerde with default settings. + * + * @return the created JsonbSerde + */ + @Override + public JsonbSerde of(final ContentType contentType) { + if (Objects.requireNonNull(contentType) == ContentType.JSON) { + return new JsonbSerde(); + } + throw new IllegalArgumentException(contentType.toString()); + } + + /** + * Creates a new JsonbSerde using the provided . + * + * @param config JsonbConfig to use + * @return the created JsonbSerde + */ + static JsonbSerde create(final JsonbConfig config) { + return new JsonbSerde(config); + } +} diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/Key.java b/jsonb-serde/src/main/java/com/arangodb/serde/Key.java new file mode 100644 index 000000000..6e4233b3c --- /dev/null +++ b/jsonb-serde/src/main/java/com/arangodb/serde/Key.java @@ -0,0 +1,19 @@ +package com.arangodb.serde; + +import jakarta.json.bind.annotation.JsonbAnnotation; +import jakarta.json.bind.annotation.JsonbProperty; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Michele Rastelli + */ +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@JsonbAnnotation +@JsonbProperty("_key") +public @interface Key { +} diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/Rev.java b/jsonb-serde/src/main/java/com/arangodb/serde/Rev.java new file mode 100644 index 000000000..0bac94972 --- /dev/null +++ b/jsonb-serde/src/main/java/com/arangodb/serde/Rev.java @@ -0,0 +1,19 @@ +package com.arangodb.serde; + +import jakarta.json.bind.annotation.JsonbAnnotation; +import jakarta.json.bind.annotation.JsonbProperty; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Michele Rastelli + */ +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@JsonbAnnotation +@JsonbProperty("_rev") +public @interface Rev { +} diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/To.java b/jsonb-serde/src/main/java/com/arangodb/serde/To.java new file mode 100644 index 000000000..6b892e968 --- /dev/null +++ b/jsonb-serde/src/main/java/com/arangodb/serde/To.java @@ -0,0 +1,19 @@ +package com.arangodb.serde; + +import jakarta.json.bind.annotation.JsonbAnnotation; +import jakarta.json.bind.annotation.JsonbProperty; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Michele Rastelli + */ +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@JsonbAnnotation +@JsonbProperty("_to") +public @interface To { +} diff --git a/jsonb-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider b/jsonb-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider new file mode 100644 index 000000000..b8fbd2996 --- /dev/null +++ b/jsonb-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider @@ -0,0 +1 @@ +com.arangodb.serde.JsonbSerdeProvider diff --git a/pom.xml b/pom.xml index 9d3220034..05652747c 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,9 @@ driver resilience-tests shaded + jackson-serde + serde-api + jsonb-serde pom diff --git a/serde-api/pom.xml b/serde-api/pom.xml new file mode 100644 index 000000000..d34d46876 --- /dev/null +++ b/serde-api/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + com.arangodb + arangodb-java-driver-parent + 7.0.0-SNAPSHOT + + + serde-api + + + 1.8 + 1.8 + UTF-8 + + + \ No newline at end of file diff --git a/driver/src/main/java/com/arangodb/ContentType.java b/serde-api/src/main/java/com/arangodb/ContentType.java similarity index 62% rename from driver/src/main/java/com/arangodb/ContentType.java rename to serde-api/src/main/java/com/arangodb/ContentType.java index ed9077ac3..ec63aaf8d 100644 --- a/driver/src/main/java/com/arangodb/ContentType.java +++ b/serde-api/src/main/java/com/arangodb/ContentType.java @@ -20,19 +20,5 @@ package com.arangodb; public enum ContentType { - JSON, VPACK; - - public static ContentType of(Protocol protocol) { - switch (protocol) { - case HTTP_JSON: - case HTTP2_JSON: - return ContentType.JSON; - case VST: - case HTTP_VPACK: - case HTTP2_VPACK: - return ContentType.VPACK; - default: - throw new IllegalArgumentException("Unexpected value: " + protocol); - } - } + JSON, VPACK } diff --git a/driver/src/main/java/com/arangodb/serde/ArangoSerde.java b/serde-api/src/main/java/com/arangodb/serde/ArangoSerde.java similarity index 84% rename from driver/src/main/java/com/arangodb/serde/ArangoSerde.java rename to serde-api/src/main/java/com/arangodb/serde/ArangoSerde.java index b8071be04..4218bbc12 100644 --- a/driver/src/main/java/com/arangodb/serde/ArangoSerde.java +++ b/serde-api/src/main/java/com/arangodb/serde/ArangoSerde.java @@ -3,7 +3,6 @@ import com.arangodb.ContentType; import java.lang.reflect.Type; -import java.util.function.Consumer; /** * Contract for serialization/deserialization of user data. @@ -13,11 +12,6 @@ * - serialization libraries already in use in frameworks (e.g. JSON-B, Micronaut Serialization, ...) * - high performance serialization libraries (e.g. supporting compile-time data binding code generation) * - low-level libraries without support to data binding - *

- * To create a custom serde based on Jackson, existing {@link JacksonSerde} can be reused and instantiated providing a - * custom configured ObjectMapper ({@link JacksonSerde#create(com.fasterxml.jackson.databind.ObjectMapper)}) or - * configured - * after creation through {@link JacksonSerde#configure(Consumer)}. */ public interface ArangoSerde { diff --git a/serde-api/src/main/java/com/arangodb/serde/ArangoSerdeProvider.java b/serde-api/src/main/java/com/arangodb/serde/ArangoSerdeProvider.java new file mode 100644 index 000000000..460e1f2fd --- /dev/null +++ b/serde-api/src/main/java/com/arangodb/serde/ArangoSerdeProvider.java @@ -0,0 +1,15 @@ +package com.arangodb.serde; + +import com.arangodb.ContentType; + +public interface ArangoSerdeProvider { + + /** + * Returns a serde instance for the given content type + * + * @param contentType content type + * @return serde instance + */ + ArangoSerde of(final ContentType contentType); + +} diff --git a/shaded/pom.xml b/shaded/pom.xml index b5d32c922..f1aa6b9f8 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -24,6 +24,11 @@ arangodb-java-driver ${project.version} + + com.arangodb + jackson-serde + ${project.version} + com.arangodb jackson-dataformat-velocypack @@ -34,11 +39,6 @@ slf4j-api 1.7.36 - - jakarta.json.bind - jakarta.json.bind-api - 2.0.0 - @@ -69,7 +69,7 @@ org.slf4j:slf4j-api - jakarta.json.bind:jakarta.json.bind-api + com.arangodb:jackson-serde @@ -83,13 +83,6 @@ com.fasterxml.jackson com.arangodb.shaded.fasterxml.jackson - - com/arangodb/serde/JacksonSerde.java - com/arangodb/internal/serde/JacksonSerdeImpl.java - com/arangodb/internal/serde/UserJsonMapperProvider.java - com/arangodb/internal/serde/UserMapperProvider.java - com/arangodb/internal/serde/UserVPackMapperProvider.java - com.arangodb.jackson.dataformat.velocypack @@ -102,13 +95,7 @@ - com.arangodb:velocypack - - META-INF/** - - - - com.arangodb:arangodb-java-driver + *:* META-INF/MANIFEST.MF From 60cf4edc63cfac456a017b2a46cdae7eeff9a219 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 6 Dec 2022 20:11:13 +0100 Subject: [PATCH 04/20] pom files reorganization --- .github/workflows/maven.yml | 2 +- driver/pom.xml | 151 +---------------- jackson-serde/pom.xml | 7 - pom.xml | 160 ++++++++++++++++++ resilience-tests/pom.xml | 46 ----- .../src/test/resources/logback-test.xml | 2 +- serde-api/pom.xml | 6 - shaded/pom.xml | 23 +-- 8 files changed, 168 insertions(+), 229 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index cce5dd8aa..4ff1b9444 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -213,7 +213,7 @@ jobs: run: mvn -version - name: Test working-directory: driver - run: mvn --no-transfer-progress test -Dadb.jackson.version=${{matrix.jackson-version}} + run: mvn --no-transfer-progress test -Djackson.version=${{matrix.jackson-version}} sonar: timeout-minutes: 10 diff --git a/driver/pom.xml b/driver/pom.xml index d31a0afc2..062549ca4 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -13,10 +13,8 @@ arangodb-java-driver arangodb-java-driver ArangoDB Java Driver - https://github.com/arangodb/arangodb-java-driver - 2.14.1 arangodb-1 https://sonarcloud.io false @@ -55,15 +53,10 @@ native - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M5 - org.graalvm.buildtools native-maven-plugin - 0.9.16 + 0.9.19 true @@ -118,10 +111,10 @@ ${project.basedir}/src/main/java/com/arangodb/PackageVersion.java.in - ${project.build.directory}/generated-sources/replacer/com/arangodb/PackageVersion.java + ${project.build.directory}/generated-sources/replacer/com/arangodb/PackageVersion.java + - @project.version@ ${project.version} @@ -158,67 +151,6 @@ false - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 - - - -Xlint:unchecked - -Xlint:deprecation - - true - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-resources-plugin - 3.3.0 - - UTF-8 - - - - org.apache.maven.plugins - maven-source-plugin - 3.2.1 - - - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.4.0 - - - attach-javadocs - - jar - - - com.arangodb.internal - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M7 - - - **/*Test.java - **/*Example.java - - - org.apache.maven.plugins maven-deploy-plugin @@ -227,46 +159,6 @@ 10 - - org.apache.maven.plugins - maven-gpg-plugin - 3.0.1 - - - sign-artifacts - verify - - sign - - - - - - org.codehaus.mojo - flatten-maven-plugin - 1.2.7 - - oss - - - - - flatten - process-resources - - flatten - - - - - flatten.clean - clean - - clean - - - - org.jacoco jacoco-maven-plugin @@ -347,7 +239,6 @@ org.slf4j slf4j-api - 1.7.36 com.fasterxml.jackson.core @@ -368,7 +259,6 @@ com.arangodb jackson-dataformat-velocypack - 3.1.0 true @@ -380,7 +270,6 @@ ch.qos.logback logback-classic - 1.2.11 test @@ -401,7 +290,6 @@ org.assertj assertj-core - 3.23.1 test @@ -424,39 +312,6 @@ - - - - com.fasterxml.jackson - jackson-bom - ${adb.jackson.version} - import - pom - - - io.vertx - vertx-stack-depchain - 4.3.4 - pom - import - - - io.netty - netty-bom - 4.1.82.Final - pom - import - - - org.junit - junit-bom - 5.9.1 - pom - import - - - - https://github.com/arangodb/arangodb-java-driver scm:git:git://github.com/arangodb/arangodb-java-driver.git diff --git a/jackson-serde/pom.xml b/jackson-serde/pom.xml index 9ad57cc61..203ba248b 100644 --- a/jackson-serde/pom.xml +++ b/jackson-serde/pom.xml @@ -11,17 +11,10 @@ jackson-serde - - 1.8 - 1.8 - UTF-8 - - com.arangodb jackson-dataformat-velocypack - 3.1.0 provided diff --git a/pom.xml b/pom.xml index 05652747c..e882ca091 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ pom arangodb-java-driver-parent + https://github.com/arangodb/arangodb-java-driver @@ -31,6 +32,7 @@ 1.8 1.8 UTF-8 + 2.14.1 @@ -50,4 +52,162 @@ https://www.arangodb.com + + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + import + pom + + + io.vertx + vertx-stack-depchain + 4.3.4 + pom + import + + + io.netty + netty-bom + 4.1.82.Final + pom + import + + + org.junit + junit-bom + 5.9.1 + pom + import + + + com.arangodb + jackson-dataformat-velocypack + 3.1.0 + + + org.slf4j + slf4j-api + 1.7.36 + + + ch.qos.logback + logback-classic + 1.2.11 + + + org.assertj + assertj-core + 3.23.1 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + + -Xlint:unchecked + -Xlint:deprecation + + true + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.0 + + UTF-8 + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.4.0 + + + attach-javadocs + + jar + + + **.internal** + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + + **/*Test.java + **/*Example.java + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.0.1 + + + sign-artifacts + verify + + sign + + + + + + org.codehaus.mojo + flatten-maven-plugin + 1.2.7 + + oss + + + + + flatten + process-resources + + flatten + + + + + flatten.clean + clean + + clean + + + + + + diff --git a/resilience-tests/pom.xml b/resilience-tests/pom.xml index 98fa04e02..54a85c96d 100644 --- a/resilience-tests/pom.xml +++ b/resilience-tests/pom.xml @@ -11,13 +11,6 @@ resilience-tests - - 17 - 17 - 17 - UTF-8 - - org.mock-server @@ -32,7 +25,6 @@ com.arangodb jackson-dataformat-velocypack - 3.1.0 eu.rekawek.toxiproxy @@ -42,12 +34,10 @@ ch.qos.logback logback-classic - 1.2.11 org.assertj assertj-core - 3.23.1 org.junit.platform @@ -72,40 +62,4 @@ - - - - io.netty - netty-bom - 4.1.84.Final - import - pom - - - com.fasterxml.jackson - jackson-bom - 2.14.1 - import - pom - - - org.junit - junit-bom - 5.9.1 - pom - import - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M5 - - - - \ No newline at end of file diff --git a/resilience-tests/src/test/resources/logback-test.xml b/resilience-tests/src/test/resources/logback-test.xml index 343f4dfdd..840d133d7 100644 --- a/resilience-tests/src/test/resources/logback-test.xml +++ b/resilience-tests/src/test/resources/logback-test.xml @@ -6,7 +6,7 @@ - + diff --git a/serde-api/pom.xml b/serde-api/pom.xml index d34d46876..c567b1159 100644 --- a/serde-api/pom.xml +++ b/serde-api/pom.xml @@ -11,10 +11,4 @@ serde-api - - 1.8 - 1.8 - UTF-8 - - \ No newline at end of file diff --git a/shaded/pom.xml b/shaded/pom.xml index f1aa6b9f8..6dbe85e10 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -12,11 +12,6 @@ arangodb-java-driver-shaded arangodb-java-driver-shaded ArangoDB Java Driver Shaded - https://github.com/arangodb/arangodb-java-driver - - - UTF-8 - @@ -26,33 +21,21 @@ com.arangodb - jackson-serde - ${project.version} + jackson-dataformat-velocypack com.arangodb - jackson-dataformat-velocypack - 3.1.0 + jackson-serde + ${project.version} org.slf4j slf4j-api - 1.7.36 - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 - - true - 1.8 - 1.8 - - maven-shade-plugin 3.4.1 From b4eb9e376cd5d01217b17c13d8d3cf234e0a7c1b Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 6 Dec 2022 20:29:42 +0100 Subject: [PATCH 05/20] deps upd --- driver/pom.xml | 6 +++--- pom.xml | 8 ++++---- .../src/test/java/resilience/SingleServerTest.java | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/driver/pom.xml b/driver/pom.xml index 062549ca4..877e1f7d2 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -181,7 +181,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.7.1.1 + 4.7.3.0 spotbugs/spotbugs-exclude.xml @@ -197,7 +197,7 @@ com.github.spotbugs spotbugs - 4.7.1 + 4.7.3 @@ -301,7 +301,7 @@ org.graalvm.sdk graal-sdk - 22.2.0 + 22.3.0 test diff --git a/pom.xml b/pom.xml index e882ca091..c01553900 100644 --- a/pom.xml +++ b/pom.xml @@ -64,14 +64,14 @@ io.vertx vertx-stack-depchain - 4.3.4 + 4.3.5 pom import io.netty netty-bom - 4.1.82.Final + 4.1.85.Final pom import @@ -90,12 +90,12 @@ org.slf4j slf4j-api - 1.7.36 + 2.0.5 ch.qos.logback logback-classic - 1.2.11 + 1.4.5 org.assertj diff --git a/resilience-tests/src/test/java/resilience/SingleServerTest.java b/resilience-tests/src/test/java/resilience/SingleServerTest.java index 7e9bc32b6..05c87d7e2 100644 --- a/resilience-tests/src/test/java/resilience/SingleServerTest.java +++ b/resilience-tests/src/test/java/resilience/SingleServerTest.java @@ -18,7 +18,7 @@ public abstract class SingleServerTest { protected static final String HOST = "127.0.0.1"; protected static final String PASSWORD = "test"; - protected static final MemoryAppender logs = new MemoryAppender(Level.DEBUG); + protected static final MemoryAppender logs = new MemoryAppender(Level.INFO); private static final Endpoint endpoint = new Endpoint("singleServer", HOST, 18529, "172.28.0.1:8529"); @BeforeAll From 94dde33ea260c878d9e7bbbd13a1e1bbf1f85f59 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 6 Dec 2022 20:53:05 +0100 Subject: [PATCH 06/20] pom files reorganization --- driver/pom.xml | 17 ----------------- pom.xml | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/driver/pom.xml b/driver/pom.xml index 877e1f7d2..f8741ce03 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -84,17 +84,6 @@ - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - @@ -312,10 +301,4 @@ - - https://github.com/arangodb/arangodb-java-driver - scm:git:git://github.com/arangodb/arangodb-java-driver.git - scm:git:git://github.com/arangodb/arangodb-java-driver.git - - diff --git a/pom.xml b/pom.xml index c01553900..af8e98249 100644 --- a/pom.xml +++ b/pom.xml @@ -144,7 +144,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.0 + 3.4.1 attach-javadocs @@ -185,7 +185,7 @@ org.codehaus.mojo flatten-maven-plugin - 1.2.7 + 1.3.0 oss @@ -208,6 +208,44 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + 3.1.0 + + + enforce + + enforce + + + + + 3.6 + + + + + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + https://github.com/arangodb/arangodb-java-driver + scm:git:git://github.com/arangodb/arangodb-java-driver.git + scm:git:git://github.com/arangodb/arangodb-java-driver.git + + From 7475be111b60de9824e4ab5a10a7d13632db738f Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 6 Dec 2022 21:20:35 +0100 Subject: [PATCH 07/20] pom files reorganization --- driver/pom.xml | 29 +++++++++++++++++++++++++++++ jackson-serde/pom.xml | 14 +++++++++++++- jsonb-serde/pom.xml | 1 + shaded/pom.xml | 8 +++++++- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/driver/pom.xml b/driver/pom.xml index f8741ce03..aabeb4643 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -211,6 +211,35 @@ + + maven-shade-plugin + 3.4.1 + + + package + + shade + + + true + true + + + com.arangodb:serde-api + + + + + com.arangodb:serde-api + + META-INF/MANIFEST.MF + + + + + + + diff --git a/jackson-serde/pom.xml b/jackson-serde/pom.xml index 203ba248b..c7ee5113e 100644 --- a/jackson-serde/pom.xml +++ b/jackson-serde/pom.xml @@ -12,10 +12,22 @@ jackson-serde + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + com.arangodb jackson-dataformat-velocypack - provided + true com.arangodb diff --git a/jsonb-serde/pom.xml b/jsonb-serde/pom.xml index aa32fda65..948b7dcd3 100644 --- a/jsonb-serde/pom.xml +++ b/jsonb-serde/pom.xml @@ -22,6 +22,7 @@ com.arangodb serde-api ${project.version} + provided jakarta.json.bind diff --git a/shaded/pom.xml b/shaded/pom.xml index 6dbe85e10..25807bd48 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -78,7 +78,13 @@ - *:* + com.arangodb:velocypack + + META-INF/** + + + + com.arangodb:arangodb-java-driver META-INF/MANIFEST.MF From 7312a5b01f0ac58f75fdef7b69bf2897aa7b7a7d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 7 Dec 2022 13:10:11 +0100 Subject: [PATCH 08/20] shaded integration tests --- driver/pom.xml | 27 --- jackson-serde/pom.xml | 1 - jsonb-serde/pom.xml | 7 - pom.xml | 53 +++++ resilience-tests/pom.xml | 25 --- shaded-integration-tests/pom.xml | 189 ++++++++++++++++++ .../jackson/java/com/arangodb/BaseTest.java | 50 +++++ .../jackson/java/com/arangodb/DbTest.java | 17 ++ .../java/com/arangodb/serde/Person.java | 12 ++ .../java/com/arangodb/serde/SerdeTest.java | 82 ++++++++ .../jsonb/java/com/arangodb/BaseTest.java | 70 +++++++ .../test/jsonb/java/com/arangodb/DbTest.java | 17 ++ .../jsonb/java/com/arangodb/serde/Person.java | 60 ++++++ .../java/com/arangodb/serde/SerdeTest.java | 69 +++++++ shaded/pom.xml | 2 - 15 files changed, 619 insertions(+), 62 deletions(-) create mode 100644 shaded-integration-tests/pom.xml create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/DbTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/SerdeTest.java create mode 100644 shaded-integration-tests/src/test/jsonb/java/com/arangodb/BaseTest.java create mode 100644 shaded-integration-tests/src/test/jsonb/java/com/arangodb/DbTest.java create mode 100644 shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java create mode 100644 shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/SerdeTest.java diff --git a/driver/pom.xml b/driver/pom.xml index aabeb4643..39391cccb 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -247,12 +247,10 @@ com.arangodb serde-api - ${project.version} com.arangodb jackson-serde - ${project.version} org.slf4j @@ -285,31 +283,6 @@ 3.0.2 provided - - ch.qos.logback - logback-classic - test - - - org.junit.platform - junit-platform-launcher - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.jupiter - junit-jupiter-params - test - - - org.assertj - assertj-core - test - org.reflections reflections diff --git a/jackson-serde/pom.xml b/jackson-serde/pom.xml index c7ee5113e..2681352c8 100644 --- a/jackson-serde/pom.xml +++ b/jackson-serde/pom.xml @@ -32,7 +32,6 @@ com.arangodb serde-api - ${project.version} provided diff --git a/jsonb-serde/pom.xml b/jsonb-serde/pom.xml index 948b7dcd3..6be681942 100644 --- a/jsonb-serde/pom.xml +++ b/jsonb-serde/pom.xml @@ -11,17 +11,10 @@ jsonb-serde - - 1.8 - 1.8 - UTF-8 - - com.arangodb serde-api - ${project.version} provided diff --git a/pom.xml b/pom.xml index af8e98249..ad9dd2f4f 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,39 @@ https://www.arangodb.com + + + ch.qos.logback + logback-classic + test + + + org.junit.platform + junit-platform-launcher + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.assertj + assertj-core + test + + + @@ -82,6 +115,26 @@ pom import + + com.arangodb + arangodb-java-driver + ${project.version} + + + com.arangodb + serde-api + ${project.version} + + + com.arangodb + jackson-serde + ${project.version} + + + com.arangodb + arangodb-java-driver-shaded + ${project.version} + com.arangodb jackson-dataformat-velocypack diff --git a/resilience-tests/pom.xml b/resilience-tests/pom.xml index 54a85c96d..341474a48 100644 --- a/resilience-tests/pom.xml +++ b/resilience-tests/pom.xml @@ -20,7 +20,6 @@ com.arangodb arangodb-java-driver - ${project.version} com.arangodb @@ -31,30 +30,6 @@ toxiproxy-java 2.1.7 - - ch.qos.logback - logback-classic - - - org.assertj - assertj-core - - - org.junit.platform - junit-platform-launcher - - - org.junit.jupiter - junit-jupiter-api - - - org.junit.jupiter - junit-jupiter-engine - - - org.junit.jupiter - junit-jupiter-params - org.awaitility awaitility diff --git a/shaded-integration-tests/pom.xml b/shaded-integration-tests/pom.xml new file mode 100644 index 000000000..e95bf5efc --- /dev/null +++ b/shaded-integration-tests/pom.xml @@ -0,0 +1,189 @@ + + + 4.0.0 + + com.arangodb + integration-tests + 7.0.0-SNAPSHOT + + + 19 + 19 + UTF-8 + 2.14.1 + + + + + ch.qos.logback + logback-classic + 1.4.5 + test + + + org.junit.platform + junit-platform-launcher + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.assertj + assertj-core + 3.23.1 + test + + + + + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + import + pom + + + io.vertx + vertx-stack-depchain + 4.3.5 + pom + import + + + io.netty + netty-bom + 4.1.85.Final + pom + import + + + org.junit + junit-bom + 5.9.1 + pom + import + + + + + + ${testSourceDirectory} + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${serde} + + + + + + + + + jackson-serde + + true + + + src/test/jackson/java + jackson + + + + com.arangodb + arangodb-java-driver-shaded + 7.0.0-SNAPSHOT + test + + + com.arangodb + jackson-dataformat-velocypack + 3.1.0 + test + + + + + jsonb-serde + + src/test/jsonb/java + jsonb + + + + com.arangodb + arangodb-java-driver-shaded + 7.0.0-SNAPSHOT + test + + + com.arangodb + jackson-serde + + + + + com.arangodb + jsonb-serde + 7.0.0-SNAPSHOT + test + + + org.eclipse + yasson + 3.0.2 + + + + + native + + + + org.graalvm.buildtools + native-maven-plugin + 0.9.19 + true + + + test-native + + generateTestResourceConfig + test + + test + + + + true + false + + --no-fallback + + + + + + + + \ No newline at end of file diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseTest.java new file mode 100644 index 000000000..0855a4e41 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseTest.java @@ -0,0 +1,50 @@ +package com.arangodb; + +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Arrays; +import java.util.stream.Stream; + +public class BaseTest { + protected static final DbName TEST_DB = DbName.of("java_driver_integration_tests"); + protected static final String HOST = "172.28.0.1"; + protected static final int PORT = 8529; + protected static final String PASSWD = "test"; + + protected static ArangoDB createAdb() { + return new ArangoDB.Builder() + .host(HOST, PORT) + .password(PASSWD) + .build(); + } + + protected static ArangoDB createAdb(ContentType contentType) { + Protocol protocol = contentType == ContentType.VPACK ? Protocol.HTTP2_VPACK : Protocol.HTTP2_JSON; + return new ArangoDB.Builder() + .host(HOST, PORT) + .password(PASSWD) + .useProtocol(protocol) + .build(); + } + + protected static ArangoDB createAdb(Protocol protocol) { + return new ArangoDB.Builder() + .host(HOST, PORT) + .password(PASSWD) + .useProtocol(protocol) + .build(); + } + + protected static Stream adbByProtocol() { + return Arrays.stream(Protocol.values()) + .map(BaseTest::createAdb) + .map(Arguments::of); + } + + protected static Stream adbByContentType() { + return Arrays.stream(ContentType.values()) + .map(BaseTest::createAdb) + .map(Arguments::of); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/DbTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/DbTest.java new file mode 100644 index 000000000..18621e366 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/DbTest.java @@ -0,0 +1,17 @@ +package com.arangodb; + +import com.arangodb.entity.ArangoDBVersion; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class DbTest extends BaseTest { + @ParameterizedTest + @MethodSource("adbByProtocol") + void getVersion(ArangoDB adb) { + final ArangoDBVersion version = adb.getVersion(); + assertThat(version.getServer()).isNotNull(); + assertThat(version.getVersion()).isNotNull(); + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java new file mode 100644 index 000000000..59ace4b67 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java @@ -0,0 +1,12 @@ +package com.arangodb.serde; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record Person( + @Key + String key, + @JsonProperty("firstName") + String name, + int age +) { +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/SerdeTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/SerdeTest.java new file mode 100644 index 000000000..8b80e637f --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/SerdeTest.java @@ -0,0 +1,82 @@ +package com.arangodb.serde; + +import com.arangodb.ArangoDB; +import com.arangodb.BaseTest; +import com.arangodb.shaded.fasterxml.jackson.databind.JsonNode; +import com.arangodb.shaded.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.arangodb.util.RawJson; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Collections; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class SerdeTest extends BaseTest { + + @ParameterizedTest + @MethodSource("adbByContentType") + void shadedJsonNode(ArangoDB adb) { + // uses the internal serde + JsonNode doc = JsonNodeFactory.instance + .objectNode() + .put("foo", "bar"); + JsonNode res = adb.db().query("return @d", Collections.singletonMap("d", doc), JsonNode.class).next(); + assertThat(res.size()).isEqualTo(1); + assertThat(res.get("foo").asText()).isEqualTo("bar"); + JsonNode value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), JsonNode.class).next(); + assertThat(value.textValue()).isEqualTo("bar"); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void jsonNode(ArangoDB adb) { + // uses the user serde + com.fasterxml.jackson.databind.JsonNode doc = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance + .objectNode() + .put("foo", "bar"); + com.fasterxml.jackson.databind.JsonNode res = adb.db().query("return @d", Collections.singletonMap("d", doc), + com.fasterxml.jackson.databind.JsonNode.class).next(); + assertThat(res.size()).isEqualTo(1); + assertThat(res.get("foo").asText()).isEqualTo("bar"); + com.fasterxml.jackson.databind.JsonNode value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), + com.fasterxml.jackson.databind.JsonNode.class).next(); + assertThat(value.textValue()).isEqualTo("bar"); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void map(ArangoDB adb) { + Map doc = Collections.singletonMap("foo", "bar"); + Map res = adb.db().query("return @d", Collections.singletonMap("d", doc), Map.class).next(); + assertThat(res).hasSize(1); + assertThat(res.get("foo")).isEqualTo("bar"); + String value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), String.class).next(); + assertThat(value).isEqualTo("bar"); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void rawJson(ArangoDB adb) { + RawJson doc = RawJson.of(""" + {"foo":"bar"}"""); + RawJson res = adb.db().query("return @d", Collections.singletonMap("d", doc), RawJson.class).next(); + assertThat(res.getValue()).isEqualTo(doc.getValue()); + RawJson value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), RawJson.class).next(); + assertThat(value.getValue()).isEqualTo("\"bar\""); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void person(ArangoDB adb) { + Person doc = new Person("key", "Jim", 22); + Person res = adb.db().query("return @d", Collections.singletonMap("d", doc), Person.class).next(); + assertThat(res).isEqualTo(doc); + String key = adb.db().query("return @d._key", Collections.singletonMap("d", doc), String.class).next(); + assertThat(key).isEqualTo("key"); + String name = adb.db().query("return @d.firstName", Collections.singletonMap("d", doc), String.class).next(); + assertThat(name).isEqualTo("Jim"); + } + +} diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/BaseTest.java b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/BaseTest.java new file mode 100644 index 000000000..5546cbe4e --- /dev/null +++ b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/BaseTest.java @@ -0,0 +1,70 @@ +package com.arangodb; + +import com.arangodb.internal.serde.ContentTypeFactory; +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Arrays; +import java.util.Locale; +import java.util.stream.Stream; + +public class BaseTest { + protected static final DbName TEST_DB = DbName.of("java_driver_integration_tests"); + protected static final String HOST = "172.28.0.1"; + protected static final int PORT = 8529; + protected static final String PASSWD = "test"; + private static final Serde serde = Serde.valueOf(System.getProperty("serde").toUpperCase(Locale.ROOT)); + + private enum Serde { + JACKSON, JSONB + } + + protected static ArangoDB createAdb() { + return new ArangoDB.Builder() + .host(HOST, PORT) + .password(PASSWD) + .build(); + } + + protected static ArangoDB createAdb(ContentType contentType) { + Protocol protocol = contentType == ContentType.VPACK ? Protocol.HTTP2_VPACK : Protocol.HTTP2_JSON; + return new ArangoDB.Builder() + .host(HOST, PORT) + .password(PASSWD) + .useProtocol(protocol) + .build(); + } + + protected static ArangoDB createAdb(Protocol protocol) { + return new ArangoDB.Builder() + .host(HOST, PORT) + .password(PASSWD) + .useProtocol(protocol) + .build(); + } + + protected static Stream adbByProtocol() { + return Arrays.stream(Protocol.values()) + .filter(BaseTest::isProtocolSupported) + .map(BaseTest::createAdb) + .map(Arguments::of); + } + + protected static Stream adbByContentType() { + return Arrays.stream(ContentType.values()) + .filter(BaseTest::isContentTypeSupported) + .map(BaseTest::createAdb) + .map(Arguments::of); + } + + private static boolean isProtocolSupported(Protocol protocol) { + return isContentTypeSupported(ContentTypeFactory.of(protocol)); + } + + private static boolean isContentTypeSupported(ContentType contentType) { + if (serde == Serde.JACKSON) { + return true; + } else { + return contentType == ContentType.JSON; + } + } +} diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/DbTest.java b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/DbTest.java new file mode 100644 index 000000000..18621e366 --- /dev/null +++ b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/DbTest.java @@ -0,0 +1,17 @@ +package com.arangodb; + +import com.arangodb.entity.ArangoDBVersion; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class DbTest extends BaseTest { + @ParameterizedTest + @MethodSource("adbByProtocol") + void getVersion(ArangoDB adb) { + final ArangoDBVersion version = adb.getVersion(); + assertThat(version.getServer()).isNotNull(); + assertThat(version.getVersion()).isNotNull(); + } +} diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java new file mode 100644 index 000000000..32d126cc5 --- /dev/null +++ b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java @@ -0,0 +1,60 @@ +package com.arangodb.serde; + + +import jakarta.json.bind.annotation.JsonbProperty; + +import java.util.Objects; + +public class Person { + @Key + private String key; + @JsonbProperty("firstName") + private String name; + private int age; + + public Person() { + } + + public Person(String key, String name, int age) { + this.key = key; + this.name = name; + this.age = age; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Person person = (Person) o; + return age == person.age && Objects.equals(key, person.key) && Objects.equals(name, person.name); + } + + @Override + public int hashCode() { + return Objects.hash(key, name, age); + } +} \ No newline at end of file diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/SerdeTest.java b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/SerdeTest.java new file mode 100644 index 000000000..184b740b0 --- /dev/null +++ b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/SerdeTest.java @@ -0,0 +1,69 @@ +package com.arangodb.serde; + +import com.arangodb.ArangoDB; +import com.arangodb.BaseTest; +import com.arangodb.shaded.fasterxml.jackson.databind.JsonNode; +import com.arangodb.shaded.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.arangodb.util.RawJson; +import jakarta.json.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Collections; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class SerdeTest extends BaseTest { + + @ParameterizedTest + @MethodSource("adbByContentType") + void jsonNode(ArangoDB adb) { + JsonObject doc = Json.createObjectBuilder() + .add("foo", "bar") + .build(); + JsonObject res = adb.db().query("return @d", Collections.singletonMap("d", doc), JsonObject.class).next(); + assertThat(res.size()).isEqualTo(1); + assertThat(res.getString("foo")).isEqualTo("bar"); + JsonValue value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), JsonValue.class).next(); + assertThat(value) + .isInstanceOf(JsonString.class) + .extracting(v -> ((JsonString) v).getString()) + .isEqualTo("bar"); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void map(ArangoDB adb) { + Map doc = Collections.singletonMap("foo", "bar"); + Map res = adb.db().query("return @d", Collections.singletonMap("d", doc), Map.class).next(); + assertThat(res).hasSize(1); + assertThat(res.get("foo")).isEqualTo("bar"); + String value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), String.class).next(); + assertThat(value).isEqualTo("bar"); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void rawJson(ArangoDB adb) { + RawJson doc = RawJson.of(""" + {"foo":"bar"}"""); + RawJson res = adb.db().query("return @d", Collections.singletonMap("d", doc), RawJson.class).next(); + assertThat(res.getValue()).isEqualTo(doc.getValue()); + RawJson value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), RawJson.class).next(); + assertThat(value.getValue()).isEqualTo("\"bar\""); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void person(ArangoDB adb) { + Person doc = new Person("key", "Jim", 22); + Person res = adb.db().query("return @d", Collections.singletonMap("d", doc), Person.class).next(); + assertThat(res).isEqualTo(doc); + String key = adb.db().query("return @d._key", Collections.singletonMap("d", doc), String.class).next(); + assertThat(key).isEqualTo("key"); + String name = adb.db().query("return @d.firstName", Collections.singletonMap("d", doc), String.class).next(); + assertThat(name).isEqualTo("Jim"); + } + +} diff --git a/shaded/pom.xml b/shaded/pom.xml index 25807bd48..d09b974c1 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -17,7 +17,6 @@ com.arangodb arangodb-java-driver - ${project.version} com.arangodb @@ -26,7 +25,6 @@ com.arangodb jackson-serde - ${project.version} org.slf4j From fc6577ac51afee675670b8f14afb26885db6c04a Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 7 Dec 2022 15:41:15 +0100 Subject: [PATCH 09/20] repackaged serde modules --- .../src/main/java/com/arangodb/async/ArangoDBAsync.java | 1 - .../src/main/java/com/arangodb/entity/DocumentEntity.java | 6 +++--- .../src/test/java/com/arangodb/ArangoCollectionTest.java | 8 ++++---- .../java/com/arangodb/async/ArangoCollectionTest.java | 4 ++-- .../java/com/arangodb/async/example/graph/Circle.java | 6 +++--- .../java/com/arangodb/async/example/graph/CircleEdge.java | 2 +- .../java/com/arangodb/async/serde/CustomSerdeTest.java | 4 ++-- .../src/test/java/com/arangodb/example/graph/Circle.java | 6 +++--- .../test/java/com/arangodb/example/graph/CircleEdge.java | 2 +- .../com/arangodb/mapping/annotations/AnnotatedEntity.java | 2 +- .../mapping/annotations/ArangoAnnotationsTest.java | 2 +- .../src/test/java/com/arangodb/serde/CustomSerdeTest.java | 2 ++ .../test/java/com/arangodb/serde/CustomTypeHintTest.java | 1 + .../main/java/com/arangodb/serde/{ => jackson}/From.java | 2 +- .../main/java/com/arangodb/serde/{ => jackson}/Id.java | 2 +- .../com/arangodb/serde/{ => jackson}/JacksonSerde.java | 3 ++- .../serde/{ => jackson}/JacksonSerdeProvider.java | 7 ++++--- .../main/java/com/arangodb/serde/{ => jackson}/Key.java | 2 +- .../main/java/com/arangodb/serde/{ => jackson}/Rev.java | 2 +- .../main/java/com/arangodb/serde/{ => jackson}/To.java | 2 +- .../jackson/internal}/JacksonMapperProvider.java | 2 +- .../jackson/internal}/JacksonSerdeImpl.java | 4 ++-- .../jackson/internal}/JsonJacksonMapperProvider.java | 2 +- .../jackson/internal}/VPackJacksonMapperProvider.java | 2 +- .../services/com.arangodb.serde.ArangoSerdeProvider | 2 +- .../main/java/com/arangodb/serde/{ => jsonb}/From.java | 2 +- .../src/main/java/com/arangodb/serde/{ => jsonb}/Id.java | 2 +- .../java/com/arangodb/serde/{ => jsonb}/JsonbSerde.java | 3 ++- .../arangodb/serde/{ => jsonb}/JsonbSerdeProvider.java | 3 ++- .../src/main/java/com/arangodb/serde/{ => jsonb}/Key.java | 2 +- .../src/main/java/com/arangodb/serde/{ => jsonb}/Rev.java | 2 +- .../src/main/java/com/arangodb/serde/{ => jsonb}/To.java | 2 +- .../services/com.arangodb.serde.ArangoSerdeProvider | 2 +- shaded/pom.xml | 7 +------ 34 files changed, 52 insertions(+), 51 deletions(-) rename jackson-serde/src/main/java/com/arangodb/serde/{ => jackson}/From.java (94%) rename jackson-serde/src/main/java/com/arangodb/serde/{ => jackson}/Id.java (94%) rename jackson-serde/src/main/java/com/arangodb/serde/{ => jackson}/JacksonSerde.java (86%) rename jackson-serde/src/main/java/com/arangodb/serde/{ => jackson}/JacksonSerdeProvider.java (79%) rename jackson-serde/src/main/java/com/arangodb/serde/{ => jackson}/Key.java (94%) rename jackson-serde/src/main/java/com/arangodb/serde/{ => jackson}/Rev.java (94%) rename jackson-serde/src/main/java/com/arangodb/serde/{ => jackson}/To.java (94%) rename jackson-serde/src/main/java/com/arangodb/{internal/serde => serde/jackson/internal}/JacksonMapperProvider.java (93%) rename jackson-serde/src/main/java/com/arangodb/{internal/serde => serde/jackson/internal}/JacksonSerdeImpl.java (94%) rename jackson-serde/src/main/java/com/arangodb/{internal/serde => serde/jackson/internal}/JsonJacksonMapperProvider.java (86%) rename jackson-serde/src/main/java/com/arangodb/{internal/serde => serde/jackson/internal}/VPackJacksonMapperProvider.java (88%) rename jsonb-serde/src/main/java/com/arangodb/serde/{ => jsonb}/From.java (93%) rename jsonb-serde/src/main/java/com/arangodb/serde/{ => jsonb}/Id.java (93%) rename jsonb-serde/src/main/java/com/arangodb/serde/{ => jsonb}/JsonbSerde.java (91%) rename jsonb-serde/src/main/java/com/arangodb/serde/{ => jsonb}/JsonbSerdeProvider.java (90%) rename jsonb-serde/src/main/java/com/arangodb/serde/{ => jsonb}/Key.java (93%) rename jsonb-serde/src/main/java/com/arangodb/serde/{ => jsonb}/Rev.java (93%) rename jsonb-serde/src/main/java/com/arangodb/serde/{ => jsonb}/To.java (93%) diff --git a/driver/src/main/java/com/arangodb/async/ArangoDBAsync.java b/driver/src/main/java/com/arangodb/async/ArangoDBAsync.java index 64581be1c..c6eaf5db4 100644 --- a/driver/src/main/java/com/arangodb/async/ArangoDBAsync.java +++ b/driver/src/main/java/com/arangodb/async/ArangoDBAsync.java @@ -39,7 +39,6 @@ import com.arangodb.model.UserCreateOptions; import com.arangodb.model.UserUpdateOptions; import com.arangodb.serde.ArangoSerde; -import com.arangodb.serde.JacksonSerde; import javax.annotation.concurrent.ThreadSafe; import javax.net.ssl.SSLContext; diff --git a/driver/src/main/java/com/arangodb/entity/DocumentEntity.java b/driver/src/main/java/com/arangodb/entity/DocumentEntity.java index af706faa6..5316c1db5 100644 --- a/driver/src/main/java/com/arangodb/entity/DocumentEntity.java +++ b/driver/src/main/java/com/arangodb/entity/DocumentEntity.java @@ -20,9 +20,9 @@ package com.arangodb.entity; -import com.arangodb.serde.Id; -import com.arangodb.serde.Key; -import com.arangodb.serde.Rev; +import com.arangodb.serde.jackson.Id; +import com.arangodb.serde.jackson.Key; +import com.arangodb.serde.jackson.Rev; /** * @author Mark Vollmary diff --git a/driver/src/test/java/com/arangodb/ArangoCollectionTest.java b/driver/src/test/java/com/arangodb/ArangoCollectionTest.java index 3dbc25012..d32cf4992 100644 --- a/driver/src/test/java/com/arangodb/ArangoCollectionTest.java +++ b/driver/src/test/java/com/arangodb/ArangoCollectionTest.java @@ -24,10 +24,10 @@ import com.arangodb.internal.serde.SerdeUtils; import com.arangodb.model.*; import com.arangodb.model.DocumentImportOptions.OnDuplicate; -import com.arangodb.serde.Id; -import com.arangodb.serde.JacksonSerde; -import com.arangodb.serde.Key; -import com.arangodb.serde.Rev; +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; diff --git a/driver/src/test/java/com/arangodb/async/ArangoCollectionTest.java b/driver/src/test/java/com/arangodb/async/ArangoCollectionTest.java index 246660203..1e7bfd36a 100644 --- a/driver/src/test/java/com/arangodb/async/ArangoCollectionTest.java +++ b/driver/src/test/java/com/arangodb/async/ArangoCollectionTest.java @@ -25,8 +25,8 @@ import com.arangodb.internal.serde.SerdeUtils; import com.arangodb.model.*; import com.arangodb.model.DocumentImportOptions.OnDuplicate; -import com.arangodb.serde.JacksonSerde; -import com.arangodb.serde.Key; +import com.arangodb.serde.jackson.JacksonSerde; +import com.arangodb.serde.jackson.Key; import com.arangodb.util.RawData; import com.arangodb.util.RawJson; import com.fasterxml.jackson.annotation.JsonTypeInfo; diff --git a/driver/src/test/java/com/arangodb/async/example/graph/Circle.java b/driver/src/test/java/com/arangodb/async/example/graph/Circle.java index 59573e539..e828aaeb3 100644 --- a/driver/src/test/java/com/arangodb/async/example/graph/Circle.java +++ b/driver/src/test/java/com/arangodb/async/example/graph/Circle.java @@ -20,9 +20,9 @@ package com.arangodb.async.example.graph; -import com.arangodb.serde.Id; -import com.arangodb.serde.Key; -import com.arangodb.serde.Rev; +import com.arangodb.serde.jackson.Id; +import com.arangodb.serde.jackson.Key; +import com.arangodb.serde.jackson.Rev; /** * @author a-brandt diff --git a/driver/src/test/java/com/arangodb/async/example/graph/CircleEdge.java b/driver/src/test/java/com/arangodb/async/example/graph/CircleEdge.java index 30fd2ef39..bfaa65210 100644 --- a/driver/src/test/java/com/arangodb/async/example/graph/CircleEdge.java +++ b/driver/src/test/java/com/arangodb/async/example/graph/CircleEdge.java @@ -20,7 +20,7 @@ package com.arangodb.async.example.graph; -import com.arangodb.serde.*; +import com.arangodb.serde.jackson.*; /** * @author a-brandt diff --git a/driver/src/test/java/com/arangodb/async/serde/CustomSerdeTest.java b/driver/src/test/java/com/arangodb/async/serde/CustomSerdeTest.java index 91ccc0add..982381db9 100644 --- a/driver/src/test/java/com/arangodb/async/serde/CustomSerdeTest.java +++ b/driver/src/test/java/com/arangodb/async/serde/CustomSerdeTest.java @@ -28,8 +28,8 @@ import com.arangodb.internal.config.FileConfigPropertiesProvider; import com.arangodb.model.DocumentCreateOptions; import com.arangodb.ContentType; -import com.arangodb.serde.JacksonSerde; -import com.arangodb.serde.JacksonSerdeProvider; +import com.arangodb.serde.jackson.JacksonSerde; +import com.arangodb.serde.jackson.JacksonSerdeProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/driver/src/test/java/com/arangodb/example/graph/Circle.java b/driver/src/test/java/com/arangodb/example/graph/Circle.java index 88e765631..f40783bdd 100644 --- a/driver/src/test/java/com/arangodb/example/graph/Circle.java +++ b/driver/src/test/java/com/arangodb/example/graph/Circle.java @@ -20,9 +20,9 @@ package com.arangodb.example.graph; -import com.arangodb.serde.Id; -import com.arangodb.serde.Key; -import com.arangodb.serde.Rev; +import com.arangodb.serde.jackson.Id; +import com.arangodb.serde.jackson.Key; +import com.arangodb.serde.jackson.Rev; /** * @author a-brandt diff --git a/driver/src/test/java/com/arangodb/example/graph/CircleEdge.java b/driver/src/test/java/com/arangodb/example/graph/CircleEdge.java index 15508ff33..96afa9f00 100644 --- a/driver/src/test/java/com/arangodb/example/graph/CircleEdge.java +++ b/driver/src/test/java/com/arangodb/example/graph/CircleEdge.java @@ -20,7 +20,7 @@ package com.arangodb.example.graph; -import com.arangodb.serde.*; +import com.arangodb.serde.jackson.*; /** * @author a-brandt diff --git a/driver/src/test/java/com/arangodb/mapping/annotations/AnnotatedEntity.java b/driver/src/test/java/com/arangodb/mapping/annotations/AnnotatedEntity.java index 26f6a208c..f26fb6f25 100644 --- a/driver/src/test/java/com/arangodb/mapping/annotations/AnnotatedEntity.java +++ b/driver/src/test/java/com/arangodb/mapping/annotations/AnnotatedEntity.java @@ -1,6 +1,6 @@ package com.arangodb.mapping.annotations; -import com.arangodb.serde.*; +import com.arangodb.serde.jackson.*; import java.util.Objects; diff --git a/driver/src/test/java/com/arangodb/mapping/annotations/ArangoAnnotationsTest.java b/driver/src/test/java/com/arangodb/mapping/annotations/ArangoAnnotationsTest.java index 5bdf72474..084f67ba9 100644 --- a/driver/src/test/java/com/arangodb/mapping/annotations/ArangoAnnotationsTest.java +++ b/driver/src/test/java/com/arangodb/mapping/annotations/ArangoAnnotationsTest.java @@ -22,7 +22,7 @@ import com.arangodb.ContentType; import com.arangodb.serde.ArangoSerde; -import com.arangodb.serde.JacksonSerdeProvider; +import com.arangodb.serde.jackson.JacksonSerdeProvider; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; diff --git a/driver/src/test/java/com/arangodb/serde/CustomSerdeTest.java b/driver/src/test/java/com/arangodb/serde/CustomSerdeTest.java index a12cbb796..428363da6 100644 --- a/driver/src/test/java/com/arangodb/serde/CustomSerdeTest.java +++ b/driver/src/test/java/com/arangodb/serde/CustomSerdeTest.java @@ -25,6 +25,8 @@ import com.arangodb.internal.config.FileConfigPropertiesProvider; import com.arangodb.internal.serde.InternalSerde; import com.arangodb.model.DocumentCreateOptions; +import com.arangodb.serde.jackson.JacksonSerde; +import com.arangodb.serde.jackson.JacksonSerdeProvider; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; diff --git a/driver/src/test/java/com/arangodb/serde/CustomTypeHintTest.java b/driver/src/test/java/com/arangodb/serde/CustomTypeHintTest.java index d0a9cb8ae..fed63354e 100644 --- a/driver/src/test/java/com/arangodb/serde/CustomTypeHintTest.java +++ b/driver/src/test/java/com/arangodb/serde/CustomTypeHintTest.java @@ -27,6 +27,7 @@ import com.arangodb.DbName; import com.arangodb.internal.config.FileConfigPropertiesProvider; import com.arangodb.model.DocumentCreateOptions; +import com.arangodb.serde.jackson.Key; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/jackson-serde/src/main/java/com/arangodb/serde/From.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/From.java similarity index 94% rename from jackson-serde/src/main/java/com/arangodb/serde/From.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/From.java index 91b85bdb1..b84ee1fb0 100644 --- a/jackson-serde/src/main/java/com/arangodb/serde/From.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/From.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jackson; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/jackson-serde/src/main/java/com/arangodb/serde/Id.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/Id.java similarity index 94% rename from jackson-serde/src/main/java/com/arangodb/serde/Id.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/Id.java index 0132d7fad..ee1033993 100644 --- a/jackson-serde/src/main/java/com/arangodb/serde/Id.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/Id.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jackson; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerde.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/JacksonSerde.java similarity index 86% rename from jackson-serde/src/main/java/com/arangodb/serde/JacksonSerde.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/JacksonSerde.java index 888740587..35e1555ab 100644 --- a/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerde.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/JacksonSerde.java @@ -1,5 +1,6 @@ -package com.arangodb.serde; +package com.arangodb.serde.jackson; +import com.arangodb.serde.ArangoSerde; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.function.Consumer; diff --git a/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerdeProvider.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/JacksonSerdeProvider.java similarity index 79% rename from jackson-serde/src/main/java/com/arangodb/serde/JacksonSerdeProvider.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/JacksonSerdeProvider.java index 005060f17..4fcbf2fcf 100644 --- a/jackson-serde/src/main/java/com/arangodb/serde/JacksonSerdeProvider.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/JacksonSerdeProvider.java @@ -1,8 +1,9 @@ -package com.arangodb.serde; +package com.arangodb.serde.jackson; import com.arangodb.ContentType; -import com.arangodb.internal.serde.JacksonSerdeImpl; -import com.arangodb.internal.serde.JacksonMapperProvider; +import com.arangodb.serde.jackson.internal.JacksonSerdeImpl; +import com.arangodb.serde.jackson.internal.JacksonMapperProvider; +import com.arangodb.serde.ArangoSerdeProvider; import com.fasterxml.jackson.databind.ObjectMapper; public class JacksonSerdeProvider implements ArangoSerdeProvider { diff --git a/jackson-serde/src/main/java/com/arangodb/serde/Key.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/Key.java similarity index 94% rename from jackson-serde/src/main/java/com/arangodb/serde/Key.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/Key.java index 519e1af4c..94177c44d 100644 --- a/jackson-serde/src/main/java/com/arangodb/serde/Key.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/Key.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jackson; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/jackson-serde/src/main/java/com/arangodb/serde/Rev.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/Rev.java similarity index 94% rename from jackson-serde/src/main/java/com/arangodb/serde/Rev.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/Rev.java index 3915881a7..764927d6f 100644 --- a/jackson-serde/src/main/java/com/arangodb/serde/Rev.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/Rev.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jackson; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/jackson-serde/src/main/java/com/arangodb/serde/To.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/To.java similarity index 94% rename from jackson-serde/src/main/java/com/arangodb/serde/To.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/To.java index 64a1f2581..7510e2e53 100644 --- a/jackson-serde/src/main/java/com/arangodb/serde/To.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/To.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jackson; import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonMapperProvider.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonMapperProvider.java similarity index 93% rename from jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonMapperProvider.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonMapperProvider.java index e31a79503..b961b009d 100644 --- a/jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonMapperProvider.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonMapperProvider.java @@ -1,4 +1,4 @@ -package com.arangodb.internal.serde; +package com.arangodb.serde.jackson.internal; import com.arangodb.ContentType; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java similarity index 94% rename from jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java index e870f7413..5c6247c2a 100644 --- a/jackson-serde/src/main/java/com/arangodb/internal/serde/JacksonSerdeImpl.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java @@ -1,6 +1,6 @@ -package com.arangodb.internal.serde; +package com.arangodb.serde.jackson.internal; -import com.arangodb.serde.JacksonSerde; +import com.arangodb.serde.jackson.JacksonSerde; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/jackson-serde/src/main/java/com/arangodb/internal/serde/JsonJacksonMapperProvider.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JsonJacksonMapperProvider.java similarity index 86% rename from jackson-serde/src/main/java/com/arangodb/internal/serde/JsonJacksonMapperProvider.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JsonJacksonMapperProvider.java index cc6ec3a3a..c2e1c4afd 100644 --- a/jackson-serde/src/main/java/com/arangodb/internal/serde/JsonJacksonMapperProvider.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JsonJacksonMapperProvider.java @@ -1,4 +1,4 @@ -package com.arangodb.internal.serde; +package com.arangodb.serde.jackson.internal; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/jackson-serde/src/main/java/com/arangodb/internal/serde/VPackJacksonMapperProvider.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/VPackJacksonMapperProvider.java similarity index 88% rename from jackson-serde/src/main/java/com/arangodb/internal/serde/VPackJacksonMapperProvider.java rename to jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/VPackJacksonMapperProvider.java index aa0fda5fa..d44ed0a2d 100644 --- a/jackson-serde/src/main/java/com/arangodb/internal/serde/VPackJacksonMapperProvider.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/VPackJacksonMapperProvider.java @@ -1,4 +1,4 @@ -package com.arangodb.internal.serde; +package com.arangodb.serde.jackson.internal; import com.arangodb.jackson.dataformat.velocypack.VPackMapper; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/jackson-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider b/jackson-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider index 88eb1d8bd..198620d5b 100644 --- a/jackson-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider +++ b/jackson-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider @@ -1 +1 @@ -com.arangodb.serde.JacksonSerdeProvider +com.arangodb.serde.jackson.JacksonSerdeProvider diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/From.java b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/From.java similarity index 93% rename from jsonb-serde/src/main/java/com/arangodb/serde/From.java rename to jsonb-serde/src/main/java/com/arangodb/serde/jsonb/From.java index 31b828dd0..057be2105 100644 --- a/jsonb-serde/src/main/java/com/arangodb/serde/From.java +++ b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/From.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jsonb; import jakarta.json.bind.annotation.JsonbAnnotation; import jakarta.json.bind.annotation.JsonbProperty; diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/Id.java b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Id.java similarity index 93% rename from jsonb-serde/src/main/java/com/arangodb/serde/Id.java rename to jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Id.java index 52ef9b21d..f2c4f8462 100644 --- a/jsonb-serde/src/main/java/com/arangodb/serde/Id.java +++ b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Id.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jsonb; import jakarta.json.bind.annotation.JsonbAnnotation; import jakarta.json.bind.annotation.JsonbProperty; diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerde.java b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/JsonbSerde.java similarity index 91% rename from jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerde.java rename to jsonb-serde/src/main/java/com/arangodb/serde/jsonb/JsonbSerde.java index 0bc6a2f2d..45c98c0bd 100644 --- a/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerde.java +++ b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/JsonbSerde.java @@ -1,5 +1,6 @@ -package com.arangodb.serde; +package com.arangodb.serde.jsonb; +import com.arangodb.serde.ArangoSerde; import jakarta.json.bind.Jsonb; import jakarta.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbConfig; diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerdeProvider.java b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/JsonbSerdeProvider.java similarity index 90% rename from jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerdeProvider.java rename to jsonb-serde/src/main/java/com/arangodb/serde/jsonb/JsonbSerdeProvider.java index 8aaa56c2e..149aa1427 100644 --- a/jsonb-serde/src/main/java/com/arangodb/serde/JsonbSerdeProvider.java +++ b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/JsonbSerdeProvider.java @@ -1,6 +1,7 @@ -package com.arangodb.serde; +package com.arangodb.serde.jsonb; import com.arangodb.ContentType; +import com.arangodb.serde.ArangoSerdeProvider; import jakarta.json.bind.JsonbConfig; import java.util.Objects; diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/Key.java b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Key.java similarity index 93% rename from jsonb-serde/src/main/java/com/arangodb/serde/Key.java rename to jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Key.java index 6e4233b3c..e5ff89558 100644 --- a/jsonb-serde/src/main/java/com/arangodb/serde/Key.java +++ b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Key.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jsonb; import jakarta.json.bind.annotation.JsonbAnnotation; import jakarta.json.bind.annotation.JsonbProperty; diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/Rev.java b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Rev.java similarity index 93% rename from jsonb-serde/src/main/java/com/arangodb/serde/Rev.java rename to jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Rev.java index 0bac94972..86a7a5ee1 100644 --- a/jsonb-serde/src/main/java/com/arangodb/serde/Rev.java +++ b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/Rev.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jsonb; import jakarta.json.bind.annotation.JsonbAnnotation; import jakarta.json.bind.annotation.JsonbProperty; diff --git a/jsonb-serde/src/main/java/com/arangodb/serde/To.java b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/To.java similarity index 93% rename from jsonb-serde/src/main/java/com/arangodb/serde/To.java rename to jsonb-serde/src/main/java/com/arangodb/serde/jsonb/To.java index 6b892e968..89b4ef7d2 100644 --- a/jsonb-serde/src/main/java/com/arangodb/serde/To.java +++ b/jsonb-serde/src/main/java/com/arangodb/serde/jsonb/To.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package com.arangodb.serde.jsonb; import jakarta.json.bind.annotation.JsonbAnnotation; import jakarta.json.bind.annotation.JsonbProperty; diff --git a/jsonb-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider b/jsonb-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider index b8fbd2996..7a37a64b1 100644 --- a/jsonb-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider +++ b/jsonb-serde/src/main/resources/META-INF/services/com.arangodb.serde.ArangoSerdeProvider @@ -1 +1 @@ -com.arangodb.serde.JsonbSerdeProvider +com.arangodb.serde.jsonb.JsonbSerdeProvider diff --git a/shaded/pom.xml b/shaded/pom.xml index d09b974c1..6ed69a89c 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -51,6 +51,7 @@ org.slf4j:slf4j-api com.arangodb:jackson-serde + com.arangodb:velocypack @@ -75,12 +76,6 @@ - - com.arangodb:velocypack - - META-INF/** - - com.arangodb:arangodb-java-driver From 31d97d14de9a5248f7217c0010337fafbf2f13c2 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 7 Dec 2022 15:41:54 +0100 Subject: [PATCH 10/20] archunit tests for shaded pkg --- shaded-integration-tests/pom.xml | 6 +++ .../jackson/java/arch/RelocationsTest.java | 48 +++++++++++++++++++ .../java/com/arangodb/serde/Person.java | 1 + .../test/jsonb/java/arch/RelocationsTest.java | 46 ++++++++++++++++++ .../jsonb/java/com/arangodb/serde/Person.java | 1 + 5 files changed, 102 insertions(+) create mode 100644 shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java create mode 100644 shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java diff --git a/shaded-integration-tests/pom.xml b/shaded-integration-tests/pom.xml index e95bf5efc..7c4f5ece5 100644 --- a/shaded-integration-tests/pom.xml +++ b/shaded-integration-tests/pom.xml @@ -48,6 +48,11 @@ 3.23.1 test + + com.tngtech.archunit + archunit-junit5 + 1.0.1 + @@ -89,6 +94,7 @@ org.apache.maven.plugins maven-surefire-plugin + 3.0.0-M7 ${serde} diff --git a/shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java b/shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java new file mode 100644 index 000000000..857b0cefa --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java @@ -0,0 +1,48 @@ +package arch; + +import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeTests; +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchRule; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + + +@AnalyzeClasses(packages = "com.arangodb..", importOptions = {DoNotIncludeTests.class}) +public class RelocationsTest { + + @ArchTest + public static final ArchRule nettyRelocation = noClasses().that() + .resideInAPackage("com.arangodb..") + .should().dependOnClassesThat() + .resideInAPackage("io.netty.."); + + @ArchTest + public static final ArchRule vertxRelocation = noClasses().that() + .resideInAPackage("com.arangodb..") + .should().dependOnClassesThat() + .resideInAPackage("io.vertx.."); + + @ArchTest + public static final ArchRule jacksonRelocation = noClasses().that() + .resideInAPackage("com.arangodb..").and() + .resideOutsideOfPackage("com.arangodb.jackson.dataformat.velocypack..").and() + .resideOutsideOfPackage("com.arangodb.serde.jackson..") + .should().dependOnClassesThat() + .resideInAPackage("com.fasterxml.jackson.."); + + @ArchTest + public static final ArchRule jacksonDataformatVelocypackRelocation = noClasses().that() + .resideInAPackage("com.arangodb..").and() + .resideOutsideOfPackage("com.arangodb.jackson.dataformat.velocypack..").and() + .resideOutsideOfPackage("com.arangodb.serde.jackson..") + .should().dependOnClassesThat() + .resideInAPackage("com.arangodb.jackson.dataformat.velocypack.."); + + @ArchTest + public static final ArchRule noJsonbDependency = noClasses().that() + .resideInAPackage("com.arangodb..") + .should().dependOnClassesThat() + .resideInAPackage("com.arangodb.serde.jsonb.."); + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java index 59ace4b67..9c1b9f18a 100644 --- a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java @@ -1,5 +1,6 @@ package com.arangodb.serde; +import com.arangodb.serde.jackson.Key; import com.fasterxml.jackson.annotation.JsonProperty; public record Person( diff --git a/shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java b/shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java new file mode 100644 index 000000000..ce2d0bb09 --- /dev/null +++ b/shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java @@ -0,0 +1,46 @@ +package arch; + +import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeTests; +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchRule; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + + +@AnalyzeClasses(packages = "com.arangodb..", importOptions = {DoNotIncludeTests.class}) +public class RelocationsTest { + + @ArchTest + public static final ArchRule nettyRelocation = noClasses().that() + .resideInAPackage("com.arangodb..") + .should().dependOnClassesThat() + .resideInAPackage("io.netty.."); + + @ArchTest + public static final ArchRule vertxRelocation = noClasses().that() + .resideInAPackage("com.arangodb..") + .should().dependOnClassesThat() + .resideInAPackage("io.vertx.."); + + @ArchTest + public static final ArchRule noJacksonDependency = noClasses().that() + .resideInAPackage("com.arangodb..") + .should().dependOnClassesThat() + .resideInAPackage("com.fasterxml.jackson.."); + + @ArchTest + public static final ArchRule noJacksonDataformatVelocypackDependency = noClasses().that() + .resideInAPackage("com.arangodb..") + .should().dependOnClassesThat() + .resideInAPackage("com.arangodb.jackson.dataformat.velocypack.."); + + @ArchTest + public static final ArchRule noVelocypackDependency = noClasses().that() + .resideInAPackage("com.arangodb..").and() + .resideOutsideOfPackage("..velocystream..").and() + .resideOutsideOfPackage("com.arangodb.shaded.jackson.dataformat.velocypack..") + .should().dependOnClassesThat() + .resideInAPackage("com.arangodb.velocypack.."); + +} diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java index 32d126cc5..4bbf67190 100644 --- a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java +++ b/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java @@ -1,6 +1,7 @@ package com.arangodb.serde; +import com.arangodb.serde.jsonb.Key; import jakarta.json.bind.annotation.JsonbProperty; import java.util.Objects; From ba511b922da829f4e22d49381c1ab3437fd273a4 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 7 Dec 2022 20:38:29 +0100 Subject: [PATCH 11/20] moved test files --- shaded-integration-tests/pom.xml | 1 + .../java/arch/AdbTest.java} | 5 +- .../java/{com/arangodb => arch}/BaseTest.java | 6 +- .../{com/arangodb/serde => arch}/Person.java | 2 +- .../src/test/jackson/java/arch/SerdeTest.java | 81 +++++++++++++++++++ .../java/arch/AdbTest.java} | 5 +- .../java/{com/arangodb => arch}/BaseTest.java | 6 +- .../{com/arangodb/serde => arch}/Person.java | 2 +- .../arangodb/serde => arch}/SerdeTest.java | 5 +- 9 files changed, 101 insertions(+), 12 deletions(-) rename shaded-integration-tests/src/test/{jsonb/java/com/arangodb/DbTest.java => jackson/java/arch/AdbTest.java} (86%) rename shaded-integration-tests/src/test/jackson/java/{com/arangodb => arch}/BaseTest.java (91%) rename shaded-integration-tests/src/test/jackson/java/{com/arangodb/serde => arch}/Person.java (89%) create mode 100644 shaded-integration-tests/src/test/jackson/java/arch/SerdeTest.java rename shaded-integration-tests/src/test/{jackson/java/com/arangodb/DbTest.java => jsonb/java/arch/AdbTest.java} (86%) rename shaded-integration-tests/src/test/jsonb/java/{com/arangodb => arch}/BaseTest.java (94%) rename shaded-integration-tests/src/test/jsonb/java/{com/arangodb/serde => arch}/Person.java (97%) rename shaded-integration-tests/src/test/jsonb/java/{com/arangodb/serde => arch}/SerdeTest.java (93%) diff --git a/shaded-integration-tests/pom.xml b/shaded-integration-tests/pom.xml index 7c4f5ece5..563f643ee 100644 --- a/shaded-integration-tests/pom.xml +++ b/shaded-integration-tests/pom.xml @@ -52,6 +52,7 @@ com.tngtech.archunit archunit-junit5 1.0.1 + test diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/DbTest.java b/shaded-integration-tests/src/test/jackson/java/arch/AdbTest.java similarity index 86% rename from shaded-integration-tests/src/test/jsonb/java/com/arangodb/DbTest.java rename to shaded-integration-tests/src/test/jackson/java/arch/AdbTest.java index 18621e366..fc320db4e 100644 --- a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/DbTest.java +++ b/shaded-integration-tests/src/test/jackson/java/arch/AdbTest.java @@ -1,12 +1,13 @@ -package com.arangodb; +package arch; +import com.arangodb.ArangoDB; import com.arangodb.entity.ArangoDBVersion; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import static org.assertj.core.api.Assertions.assertThat; -class DbTest extends BaseTest { +class AdbTest extends BaseTest { @ParameterizedTest @MethodSource("adbByProtocol") void getVersion(ArangoDB adb) { diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseTest.java b/shaded-integration-tests/src/test/jackson/java/arch/BaseTest.java similarity index 91% rename from shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseTest.java rename to shaded-integration-tests/src/test/jackson/java/arch/BaseTest.java index 0855a4e41..3927d7fff 100644 --- a/shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseTest.java +++ b/shaded-integration-tests/src/test/jackson/java/arch/BaseTest.java @@ -1,5 +1,9 @@ -package com.arangodb; +package arch; +import com.arangodb.ArangoDB; +import com.arangodb.ContentType; +import com.arangodb.DbName; +import com.arangodb.Protocol; import org.junit.jupiter.params.provider.Arguments; import java.util.Arrays; diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java b/shaded-integration-tests/src/test/jackson/java/arch/Person.java similarity index 89% rename from shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java rename to shaded-integration-tests/src/test/jackson/java/arch/Person.java index 9c1b9f18a..a168c9d0c 100644 --- a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/Person.java +++ b/shaded-integration-tests/src/test/jackson/java/arch/Person.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package arch; import com.arangodb.serde.jackson.Key; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/shaded-integration-tests/src/test/jackson/java/arch/SerdeTest.java b/shaded-integration-tests/src/test/jackson/java/arch/SerdeTest.java new file mode 100644 index 000000000..91b2e0831 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/arch/SerdeTest.java @@ -0,0 +1,81 @@ +package arch; + +import com.arangodb.ArangoDB; +import com.arangodb.shaded.fasterxml.jackson.databind.JsonNode; +import com.arangodb.shaded.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.arangodb.util.RawJson; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Collections; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class SerdeTest extends BaseTest { + + @ParameterizedTest + @MethodSource("adbByContentType") + void shadedJsonNode(ArangoDB adb) { + // uses the internal serde + JsonNode doc = JsonNodeFactory.instance + .objectNode() + .put("foo", "bar"); + JsonNode res = adb.db().query("return @d", Collections.singletonMap("d", doc), JsonNode.class).next(); + assertThat(res.size()).isEqualTo(1); + assertThat(res.get("foo").asText()).isEqualTo("bar"); + JsonNode value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), JsonNode.class).next(); + assertThat(value.textValue()).isEqualTo("bar"); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void jsonNode(ArangoDB adb) { + // uses the user serde + com.fasterxml.jackson.databind.JsonNode doc = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance + .objectNode() + .put("foo", "bar"); + com.fasterxml.jackson.databind.JsonNode res = adb.db().query("return @d", Collections.singletonMap("d", doc), + com.fasterxml.jackson.databind.JsonNode.class).next(); + assertThat(res.size()).isEqualTo(1); + assertThat(res.get("foo").asText()).isEqualTo("bar"); + com.fasterxml.jackson.databind.JsonNode value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), + com.fasterxml.jackson.databind.JsonNode.class).next(); + assertThat(value.textValue()).isEqualTo("bar"); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void map(ArangoDB adb) { + Map doc = Collections.singletonMap("foo", "bar"); + Map res = adb.db().query("return @d", Collections.singletonMap("d", doc), Map.class).next(); + assertThat(res).hasSize(1); + assertThat(res.get("foo")).isEqualTo("bar"); + String value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), String.class).next(); + assertThat(value).isEqualTo("bar"); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void rawJson(ArangoDB adb) { + RawJson doc = RawJson.of(""" + {"foo":"bar"}"""); + RawJson res = adb.db().query("return @d", Collections.singletonMap("d", doc), RawJson.class).next(); + assertThat(res.getValue()).isEqualTo(doc.getValue()); + RawJson value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), RawJson.class).next(); + assertThat(value.getValue()).isEqualTo("\"bar\""); + } + + @ParameterizedTest + @MethodSource("adbByContentType") + void person(ArangoDB adb) { + Person doc = new Person("key", "Jim", 22); + Person res = adb.db().query("return @d", Collections.singletonMap("d", doc), Person.class).next(); + assertThat(res).isEqualTo(doc); + String key = adb.db().query("return @d._key", Collections.singletonMap("d", doc), String.class).next(); + assertThat(key).isEqualTo("key"); + String name = adb.db().query("return @d.firstName", Collections.singletonMap("d", doc), String.class).next(); + assertThat(name).isEqualTo("Jim"); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/DbTest.java b/shaded-integration-tests/src/test/jsonb/java/arch/AdbTest.java similarity index 86% rename from shaded-integration-tests/src/test/jackson/java/com/arangodb/DbTest.java rename to shaded-integration-tests/src/test/jsonb/java/arch/AdbTest.java index 18621e366..fc320db4e 100644 --- a/shaded-integration-tests/src/test/jackson/java/com/arangodb/DbTest.java +++ b/shaded-integration-tests/src/test/jsonb/java/arch/AdbTest.java @@ -1,12 +1,13 @@ -package com.arangodb; +package arch; +import com.arangodb.ArangoDB; import com.arangodb.entity.ArangoDBVersion; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import static org.assertj.core.api.Assertions.assertThat; -class DbTest extends BaseTest { +class AdbTest extends BaseTest { @ParameterizedTest @MethodSource("adbByProtocol") void getVersion(ArangoDB adb) { diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/BaseTest.java b/shaded-integration-tests/src/test/jsonb/java/arch/BaseTest.java similarity index 94% rename from shaded-integration-tests/src/test/jsonb/java/com/arangodb/BaseTest.java rename to shaded-integration-tests/src/test/jsonb/java/arch/BaseTest.java index 5546cbe4e..c6851582b 100644 --- a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/BaseTest.java +++ b/shaded-integration-tests/src/test/jsonb/java/arch/BaseTest.java @@ -1,5 +1,9 @@ -package com.arangodb; +package arch; +import com.arangodb.ArangoDB; +import com.arangodb.ContentType; +import com.arangodb.DbName; +import com.arangodb.Protocol; import com.arangodb.internal.serde.ContentTypeFactory; import org.junit.jupiter.params.provider.Arguments; diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java b/shaded-integration-tests/src/test/jsonb/java/arch/Person.java similarity index 97% rename from shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java rename to shaded-integration-tests/src/test/jsonb/java/arch/Person.java index 4bbf67190..c67dab135 100644 --- a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/Person.java +++ b/shaded-integration-tests/src/test/jsonb/java/arch/Person.java @@ -1,4 +1,4 @@ -package com.arangodb.serde; +package arch; import com.arangodb.serde.jsonb.Key; diff --git a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/SerdeTest.java b/shaded-integration-tests/src/test/jsonb/java/arch/SerdeTest.java similarity index 93% rename from shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/SerdeTest.java rename to shaded-integration-tests/src/test/jsonb/java/arch/SerdeTest.java index 184b740b0..c679a4310 100644 --- a/shaded-integration-tests/src/test/jsonb/java/com/arangodb/serde/SerdeTest.java +++ b/shaded-integration-tests/src/test/jsonb/java/arch/SerdeTest.java @@ -1,9 +1,6 @@ -package com.arangodb.serde; +package arch; import com.arangodb.ArangoDB; -import com.arangodb.BaseTest; -import com.arangodb.shaded.fasterxml.jackson.databind.JsonNode; -import com.arangodb.shaded.fasterxml.jackson.databind.node.JsonNodeFactory; import com.arangodb.util.RawJson; import jakarta.json.*; import org.junit.jupiter.params.ParameterizedTest; From 997c2edc96f5e8cb02d3cbdc1d8c4e7a8a1c6150 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 7 Dec 2022 20:56:19 +0100 Subject: [PATCH 12/20] fixed jackson serde annotations in shaded pkg --- .../java/com/arangodb/entity/DocumentEntity.java | 16 ++++++++++------ .../test/jackson/java/arch/RelocationsTest.java | 8 ++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/driver/src/main/java/com/arangodb/entity/DocumentEntity.java b/driver/src/main/java/com/arangodb/entity/DocumentEntity.java index 5316c1db5..56cc8545b 100644 --- a/driver/src/main/java/com/arangodb/entity/DocumentEntity.java +++ b/driver/src/main/java/com/arangodb/entity/DocumentEntity.java @@ -20,20 +20,24 @@ package com.arangodb.entity; -import com.arangodb.serde.jackson.Id; -import com.arangodb.serde.jackson.Key; -import com.arangodb.serde.jackson.Rev; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; /** * @author Mark Vollmary */ public class DocumentEntity { - @Key + @JsonProperty("_key") + @JsonInclude(JsonInclude.Include.NON_NULL) private String key; - @Id + + @JsonProperty("_id") + @JsonInclude(JsonInclude.Include.NON_NULL) private String id; - @Rev + + @JsonProperty("_rev") + @JsonInclude(JsonInclude.Include.NON_NULL) private String rev; public DocumentEntity() { diff --git a/shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java b/shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java index 857b0cefa..761556d78 100644 --- a/shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java +++ b/shaded-integration-tests/src/test/jackson/java/arch/RelocationsTest.java @@ -45,4 +45,12 @@ public class RelocationsTest { .should().dependOnClassesThat() .resideInAPackage("com.arangodb.serde.jsonb.."); + @ArchTest + // jackson-serde is accessed via SPI + public static final ArchRule noExplicitDependencyOnJacksonSerde = noClasses().that() + .resideInAPackage("com.arangodb..").and() + .resideOutsideOfPackage("com.arangodb.serde.jackson..") + .should().dependOnClassesThat() + .resideInAPackage("com.arangodb.serde.jackson.."); + } From 65b684ce684df131b8add958bdbc65afa58ce440 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 7 Dec 2022 21:09:26 +0100 Subject: [PATCH 13/20] fixed TransactionOptions serialization --- .../src/main/java/com/arangodb/model/TransactionOptions.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/driver/src/main/java/com/arangodb/model/TransactionOptions.java b/driver/src/main/java/com/arangodb/model/TransactionOptions.java index 95d0b5378..28d314d18 100644 --- a/driver/src/main/java/com/arangodb/model/TransactionOptions.java +++ b/driver/src/main/java/com/arangodb/model/TransactionOptions.java @@ -20,6 +20,8 @@ package com.arangodb.model; +import com.arangodb.internal.serde.UserData; + /** * @author Mark Vollmary * @author Michele Rastelli @@ -57,6 +59,7 @@ TransactionOptions action(final String action) { return this; } + @UserData public Object getParams() { return params; } From 54667a272b51d2c8113535d1da62482104cae892 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 7 Dec 2022 21:10:08 +0100 Subject: [PATCH 14/20] shaded tests --- .../com/arangodb/ArangoCollectionTest.java | 3220 +++++++++++++++++ .../java/com/arangodb/ArangoCursorTest.java | 133 + .../java/com/arangodb/ArangoDBTest.java | 656 ++++ .../java/com/arangodb/ArangoDatabaseTest.java | 1413 ++++++++ .../com/arangodb/ArangoDocumentUtilTest.java | 84 + .../arangodb/ArangoEdgeCollectionTest.java | 445 +++ .../java/com/arangodb/ArangoGraphTest.java | 438 +++ .../java/com/arangodb/ArangoSearchTest.java | 1010 ++++++ .../java/com/arangodb/ArangoSslTest.java | 101 + .../arangodb/ArangoVertexCollectionTest.java | 480 +++ .../java/com/arangodb/ArangoViewTest.java | 114 + .../jackson/java/com/arangodb/BaseJunit5.java | 128 + .../java/com/arangodb/ConcurrencyTests.java | 34 + .../java/com/arangodb/DocumentTest.java | 138 + .../java/com/arangodb/InvertedIndexTest.java | 194 + .../java/com/arangodb/JwtAuthTest.java | 97 + .../java/com/arangodb/ParallelTest.java | 42 + .../java/com/arangodb/SerializableTest.java | 57 + .../StreamTransactionConflictsTest.java | 119 + .../arangodb/StreamTransactionGraphTest.java | 407 +++ .../com/arangodb/StreamTransactionTest.java | 820 +++++ .../arangodb/annotations/AnnotatedEntity.java | 81 + .../annotations/ArangoAnnotationsTest.java | 64 + .../arangodb/async/ArangoCollectionTest.java | 2514 +++++++++++++ .../java/com/arangodb/async/ArangoDBTest.java | 566 +++ .../arangodb/async/ArangoDatabaseTest.java | 1024 ++++++ .../async/ArangoEdgeCollectionTest.java | 367 ++ .../com/arangodb/async/ArangoGraphTest.java | 387 ++ .../com/arangodb/async/ArangoRouteTest.java | 81 + .../com/arangodb/async/ArangoSearchTest.java | 551 +++ .../async/ArangoVertexCollectionTest.java | 356 ++ .../com/arangodb/async/ArangoViewTest.java | 86 + .../java/com/arangodb/async/BaseTest.java | 118 + .../com/arangodb/async/CommunicationTest.java | 46 + .../com/arangodb/async/ConcurrencyTest.java | 94 + .../com/arangodb/async/ConcurrencyTests.java | 29 + .../com/arangodb/async/InvertedIndexTest.java | 199 + .../java/com/arangodb/async/JwtAuthTest.java | 103 + .../async/StreamTransactionConflictsTest.java | 120 + .../async/StreamTransactionGraphTest.java | 398 ++ .../arangodb/async/StreamTransactionTest.java | 362 ++ .../debug/ConsolidationIntervalMsecTest.java | 83 + .../arangodb/async/example/ExampleBase.java | 64 + ...ueryWithSpecialReturnTypesExampleTest.java | 128 + .../document/GetDocumentExampleTest.java | 94 + .../document/ImportDocumentExampleTest.java | 87 + .../document/InsertDocumentExampleTest.java | 74 + .../async/example/document/TestEntity.java | 47 + .../graph/AQLActorsAndMoviesExampleTest.java | 605 ++++ .../async/example/graph/BaseGraphTest.java | 116 + .../arangodb/async/example/graph/Circle.java | 80 + .../async/example/graph/CircleEdge.java | 122 + .../GraphTraversalsInAQLExampleTest.java | 122 + .../graph/ShortestPathInAQLExampleTest.java | 106 + .../async/example/ssl/SslExampleTest.java | 79 + .../arangodb/async/serde/CustomSerdeTest.java | 177 + .../com/arangodb/example/ExampleBase.java | 62 + .../com/arangodb/example/FirstProject.java | 134 + ...ueryWithSpecialReturnTypesExampleTest.java | 132 + .../document/GetDocumentExampleTest.java | 98 + .../document/InsertDocumentExampleTest.java | 70 + .../arangodb/example/document/TestEntity.java | 47 + .../graph/AQLActorsAndMoviesExampleTest.java | 581 +++ .../arangodb/example/graph/BaseGraphTest.java | 114 + .../com/arangodb/example/graph/Circle.java | 80 + .../arangodb/example/graph/CircleEdge.java | 122 + .../GraphTraversalsInAQLExampleTest.java | 113 + .../graph/ShortestPathInAQLExampleTest.java | 105 + .../arangodb/example/ssl/SslExampleTest.java | 131 + .../arangodb/internal/HostHandlerTest.java | 189 + .../internal/QueueTimeMetricsImplTest.java | 61 + .../velocystream/CommunicationTest.java | 122 + .../com/arangodb/serde/CustomSerdeTest.java | 242 ++ .../arangodb/serde/CustomTypeHintTest.java | 145 + .../java/com/arangodb/serde/SerdeTest.java | 103 +- .../java/com/arangodb/util/MapBuilder.java | 46 + .../com/arangodb/util/MapBuilderTest.java | 43 + .../java/com/arangodb/util/TestUtils.java | 112 + .../test/resources/arangodb-bad.properties | 1 + .../src/test/resources/arangodb.properties | 5 + .../src/test/resources/logback-test.xml | 18 + 81 files changed, 22479 insertions(+), 57 deletions(-) create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoCollectionTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoCursorTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDBTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDatabaseTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDocumentUtilTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoEdgeCollectionTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoGraphTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoSearchTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoSslTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoVertexCollectionTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoViewTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseJunit5.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ConcurrencyTests.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/DocumentTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/InvertedIndexTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/JwtAuthTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/ParallelTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/SerializableTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionConflictsTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionGraphTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/annotations/AnnotatedEntity.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/annotations/ArangoAnnotationsTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoCollectionTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoDBTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoDatabaseTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoEdgeCollectionTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoGraphTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoRouteTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoSearchTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoVertexCollectionTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoViewTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/BaseTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/CommunicationTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ConcurrencyTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ConcurrencyTests.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/InvertedIndexTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/JwtAuthTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionConflictsTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionGraphTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/debug/ConsolidationIntervalMsecTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/ExampleBase.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/AqlQueryWithSpecialReturnTypesExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/GetDocumentExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/ImportDocumentExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/InsertDocumentExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/TestEntity.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/AQLActorsAndMoviesExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/BaseGraphTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/Circle.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/CircleEdge.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/GraphTraversalsInAQLExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/ShortestPathInAQLExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/ssl/SslExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/async/serde/CustomSerdeTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/ExampleBase.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/FirstProject.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/AqlQueryWithSpecialReturnTypesExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/GetDocumentExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/InsertDocumentExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/TestEntity.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/AQLActorsAndMoviesExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/BaseGraphTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/Circle.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/CircleEdge.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/GraphTraversalsInAQLExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/ShortestPathInAQLExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/example/ssl/SslExampleTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/HostHandlerTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/QueueTimeMetricsImplTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/velocystream/CommunicationTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/CustomSerdeTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/CustomTypeHintTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/util/MapBuilder.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/util/MapBuilderTest.java create mode 100644 shaded-integration-tests/src/test/jackson/java/com/arangodb/util/TestUtils.java create mode 100644 shaded-integration-tests/src/test/resources/arangodb-bad.properties create mode 100644 shaded-integration-tests/src/test/resources/arangodb.properties create mode 100644 shaded-integration-tests/src/test/resources/logback-test.xml diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoCollectionTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoCollectionTest.java new file mode 100644 index 000000000..acff3ae3b --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoCollectionTest.java @@ -0,0 +1,3220 @@ +/* + * 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.shaded.fasterxml.jackson.databind.JsonNode; +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.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.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 ArangoCollectionTest 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 cols() { + return dbsStream().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("cols") + void insertDocument(ArangoCollection collection) { + final DocumentCreateEntity doc = collection.insertDocument(new BaseDocument(), null); + 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("cols") + void insertDocumentWithArrayWithNullValues(ArangoCollection collection) { + 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)); + 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("cols") + void insertDocumentWithNullValues(ArangoCollection collection) { + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("null", null); + + final DocumentCreateEntity insertedDoc = collection.insertDocument(doc, + new DocumentCreateOptions().returnNew(true)); + 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("cols") + void insertDocumentUpdateRev(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentReturnNew(ArangoCollection collection) { + final DocumentCreateOptions options = new DocumentCreateOptions().returnNew(true); + final DocumentCreateEntity doc = collection.insertDocument(new BaseDocument(), options); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentWithTypeOverwriteModeReplace(ArangoCollection collection) { + 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); + 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("cols") + void insertDocumentOverwriteModeIgnore(ArangoCollection collection) { + 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); + + final BaseDocument doc2 = new BaseDocument(key); + doc2.addAttribute("bar", "b"); + final DocumentCreateEntity insertIgnore = collection.insertDocument(doc2, + new DocumentCreateOptions().overwriteMode(OverwriteMode.ignore)); + + assertThat(insertIgnore).isNotNull(); + assertThat(insertIgnore.getRev()).isEqualTo(meta.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentOverwriteModeConflict(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 7)); + + String key = "key-" + UUID.randomUUID(); + final BaseDocument doc = new BaseDocument(key); + doc.addAttribute("foo", "a"); + collection.insertDocument(doc); + + final BaseDocument doc2 = new BaseDocument(key); + Throwable thrown = catchThrowable(() -> collection.insertDocument(doc2, + new DocumentCreateOptions().overwriteMode(OverwriteMode.conflict))); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1210); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentOverwriteModeReplace(ArangoCollection collection) { + 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); + + final BaseDocument doc2 = new BaseDocument(key); + doc2.addAttribute("bar", "b"); + final DocumentCreateEntity repsert = collection.insertDocument(doc2, +new DocumentCreateOptions().overwriteMode(OverwriteMode.replace).returnNew(true)); + + 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("cols") + void insertDocumentOverwriteModeUpdate(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 7)); + + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("foo", "a"); + final DocumentCreateEntity meta = collection.insertDocument(doc); + + doc.addAttribute("bar", "b"); + final DocumentCreateEntity updated = collection.insertDocument(doc, + new DocumentCreateOptions().overwriteMode(OverwriteMode.update).returnNew(true)); + + 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("cols") + void insertDocumentOverwriteModeUpdateMergeObjectsFalse(ArangoCollection collection) { + 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); + + 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)); + + assertThat(updated).isNotNull(); + assertThat(updated.getRev()).isNotEqualTo(meta.getRev()); + assertThat(updated.getNew().getAttribute("foo")).isEqualTo(fieldB); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentOverwriteModeUpdateKeepNullTrue(ArangoCollection collection) { + 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)).getNew(); + + assertThat(updated.getProperties()).containsEntry("foo", null); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentOverwriteModeUpdateKeepNullFalse(ArangoCollection collection) { + 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(false) + .returnNew(true)).getNew(); + + assertThat(updated.getProperties()).doesNotContainKey("foo"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentWaitForSync(ArangoCollection collection) { + final DocumentCreateOptions options = new DocumentCreateOptions().waitForSync(true); + final DocumentCreateEntity doc = collection.insertDocument(new BaseDocument(), options); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentAsJson(ArangoCollection collection) { + String key = "doc-" + UUID.randomUUID(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\",\"a\":\"test\"}"); + final DocumentCreateEntity doc = collection.insertDocument(rawJson); + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isEqualTo(collection.name() + "/" + key); + assertThat(doc.getKey()).isEqualTo(key); + assertThat(doc.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentAsBytes(ArangoCollection collection) { + 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)); + 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().getValue(), + Map.class); + assertThat(newDoc).containsAllEntriesOf(doc); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentSilent(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final DocumentCreateEntity meta = collection.insertDocument(new BaseDocument(), + new DocumentCreateOptions().silent(true)); + assertThat(meta).isNotNull(); + assertThat(meta.getId()).isNull(); + assertThat(meta.getKey()).isNull(); + assertThat(meta.getRev()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentSilentDontTouchInstance(ArangoCollection collection) { + 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)); + assertThat(meta).isNotNull(); + assertThat(meta.getKey()).isNull(); + assertThat(doc.getKey()).isEqualTo(key); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentsSilent(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final MultiDocumentEntity> info = + collection.insertDocuments(Arrays.asList(new BaseDocument(), new BaseDocument()), +new DocumentCreateOptions().silent(true)); + assertThat(info).isNotNull(); + assertThat(info.getDocuments()).isEmpty(); + assertThat(info.getDocumentsAndErrors()).isEmpty(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocument(ArangoCollection collection) { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null); + assertThat(createResult.getKey()).isNotNull(); + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, null); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentIfMatch(ArangoCollection collection) { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifMatch(createResult.getRev()); + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, options); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentIfMatchFail(ArangoCollection collection) { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifMatch("no"); + final BaseDocument document = collection.getDocument(createResult.getKey(), BaseDocument.class, options); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentIfNoneMatch(ArangoCollection collection) { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifNoneMatch("no"); + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class, options); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentIfNoneMatchFail(ArangoCollection collection) { + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument(), null); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifNoneMatch(createResult.getRev()); + final BaseDocument document = collection.getDocument(createResult.getKey(), BaseDocument.class, options); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentAsJson(ArangoCollection collection) { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\",\"a\":\"test\"}"); + collection.insertDocument(rawJson); + final RawJson readResult = collection.getDocument(key, RawJson.class); + assertThat(readResult.getValue()).contains("\"_key\":\"" + key + "\"").contains("\"_id\":\"" + COLLECTION_NAME + "/" + key + "\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentNotFound(ArangoCollection collection) { + final BaseDocument document = collection.getDocument("no", BaseDocument.class); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentNotFoundOptionsDefault(ArangoCollection collection) { + final BaseDocument document = collection.getDocument("no", BaseDocument.class, new DocumentReadOptions()); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentNotFoundOptionsNull(ArangoCollection collection) { + final BaseDocument document = collection.getDocument("no", BaseDocument.class, null); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentWrongKey(ArangoCollection collection) { + Throwable thrown = catchThrowable(() -> collection.getDocument("no/no", BaseDocument.class)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentDirtyRead(ArangoCollection collection) throws InterruptedException { + 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)); + assertThat(document).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocuments(ArangoCollection collection) { + 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); + 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("cols") + void getDocumentsWithCustomShardingKey(ArangoCollection c) { + ArangoCollection collection = c.db().collection("customShardingKeyCollection"); + if (collection.exists()) collection.drop(); + + collection.create(new CollectionCreateOptions().shardKeys("customField").numberOfShards(10)); + + 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); + List insertedKeys = + inserted.getDocuments().stream().map(DocumentEntity::getKey).collect(Collectors.toList()); + + final Collection documents = +collection.getDocuments(insertedKeys, BaseDocument.class).getDocuments(); + + assertThat(documents).hasSize(10); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentsDirtyRead(ArangoCollection collection) { + 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)); + 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("cols") + void getDocumentsNotFound(ArangoCollection collection) { + final MultiDocumentEntity readResult = collection.getDocuments(Collections.singleton("no"), + BaseDocument.class); + assertThat(readResult).isNotNull(); + assertThat(readResult.getDocuments()).isEmpty(); + assertThat(readResult.getErrors()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getDocumentsWrongKey(ArangoCollection collection) { + final MultiDocumentEntity readResult = collection.getDocuments(Collections.singleton("no/no"), + BaseDocument.class); + assertThat(readResult).isNotNull(); + assertThat(readResult.getDocuments()).isEmpty(); + assertThat(readResult.getErrors()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocument(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, +null); + 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); + 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("cols") + void updateDocumentWithDifferentReturnType(ArangoCollection collection) { + 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); + 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("cols") + void updateDocumentUpdateRev(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.addAttribute("foo", "bar"); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + null); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(updateResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentIfMatch(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + 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); + 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); + 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("cols") + void updateDocumentIfMatchFail(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + 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)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentReturnNew(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + final DocumentUpdateOptions options = new DocumentUpdateOptions().returnNew(true); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options); + 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("cols") + void updateDocumentReturnOld(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + final DocumentUpdateOptions options = new DocumentUpdateOptions().returnOld(true); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options); + 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("cols") + void updateDocumentKeepNullTrue(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.updateAttribute("a", null); + final DocumentUpdateOptions options = new DocumentUpdateOptions().keepNull(true); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options); + 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); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentKeepNullFalse(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.updateAttribute("a", null); + final DocumentUpdateOptions options = new DocumentUpdateOptions().keepNull(false); + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), doc, + options); + 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); + 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("cols") + void updateDocumentSerializeNullTrue(ArangoCollection collection) { + final TestUpdateEntity doc = new TestUpdateEntity(); + doc.a = "foo"; + doc.b = "foo"; + final DocumentCreateEntity createResult = collection.insertDocument(doc); + final TestUpdateEntity patchDoc = new TestUpdateEntity(); + patchDoc.a = "bar"; + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), patchDoc); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getKey()).isEqualTo(createResult.getKey()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties()).containsKey("a"); + assertThat(readResult.getAttribute("a")).isEqualTo("bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentSerializeNullFalse(ArangoCollection collection) { + final TestUpdateEntitySerializeNullFalse doc = new TestUpdateEntitySerializeNullFalse(); + doc.a = "foo"; + doc.b = "foo"; + final DocumentCreateEntity createResult = collection.insertDocument(doc); + final TestUpdateEntitySerializeNullFalse patchDoc = new TestUpdateEntitySerializeNullFalse(); + patchDoc.a = "bar"; + final DocumentUpdateEntity updateResult = collection.updateDocument(createResult.getKey(), patchDoc); + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getKey()).isEqualTo(createResult.getKey()); + + final BaseDocument readResult = collection.getDocument(createResult.getKey(), BaseDocument.class); + 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("cols") + void updateDocumentMergeObjectsTrue(ArangoCollection collection) { + 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); + 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); + 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); + 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("cols") + void updateDocumentMergeObjectsFalse(ArangoCollection collection) { + 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); + 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); + 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); + 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("cols") + void updateDocumentIgnoreRevsFalse(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.updateAttribute("a", "test1"); + doc.setRevision("no"); + + final DocumentUpdateOptions options = new DocumentUpdateOptions().ignoreRevs(false); + Throwable thrown = catchThrowable(() -> collection.updateDocument(createResult.getKey(), doc, options)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentSilent(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()); + final DocumentUpdateEntity meta = collection.updateDocument(createResult.getKey(), + new BaseDocument(), new DocumentUpdateOptions().silent(true)); + assertThat(meta).isNotNull(); + assertThat(meta.getId()).isNull(); + assertThat(meta.getKey()).isNull(); + assertThat(meta.getRev()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentsSilent(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()); + final MultiDocumentEntity> info = +collection.updateDocuments(Collections.singletonList(new BaseDocument(createResult.getKey())), + new DocumentUpdateOptions().silent(true)); + assertThat(info).isNotNull(); + assertThat(info.getDocuments()).isEmpty(); + assertThat(info.getDocumentsAndErrors()).isEmpty(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateNonExistingDocument(ArangoCollection 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)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(404); + assertThat(e.getErrorNum()).isEqualTo(1202); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentPreconditionFailed(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument("test-" + rnd()); + doc.addAttribute("foo", "a"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + + doc.updateAttribute("foo", "b"); + collection.updateDocument(doc.getKey(), doc, null); + + doc.updateAttribute("foo", "c"); + Throwable thrown = catchThrowable(() -> collection.updateDocument(doc.getKey(), doc, + new DocumentUpdateOptions().ifMatch(createResult.getRev()))); + 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); + assertThat(readDocument.getAttribute("foo")).isEqualTo("b"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocument(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), +doc, null); + 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); + 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("cols") + void replaceDocumentUpdateRev(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), + doc, null); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(replaceResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentIfMatch(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().ifMatch(createResult.getRev()); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), + doc, options); + 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); + 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("cols") + void replaceDocumentIfMatchFail(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + + final DocumentReplaceOptions options = new DocumentReplaceOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> collection.replaceDocument(createResult.getKey(), doc, options)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentIgnoreRevsFalse(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + 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)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentReturnNew(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().returnNew(true); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), +doc, options); + 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("cols") + void replaceDocumentReturnOld(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().returnOld(true); + final DocumentUpdateEntity replaceResult = collection.replaceDocument(createResult.getKey(), + doc, options); + 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("cols") + void replaceDocumentSilent(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()); + final DocumentUpdateEntity meta = collection.replaceDocument(createResult.getKey(), +new BaseDocument(), new DocumentReplaceOptions().silent(true)); + assertThat(meta).isNotNull(); + assertThat(meta.getId()).isNull(); + assertThat(meta.getKey()).isNull(); + assertThat(meta.getRev()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentSilentDontTouchInstance(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc); + final DocumentUpdateEntity meta = collection.replaceDocument(createResult.getKey(), doc, + new DocumentReplaceOptions().silent(true)); + assertThat(meta.getRev()).isNull(); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentsSilent(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()); + final MultiDocumentEntity> info = + collection.replaceDocuments(Collections.singletonList(new BaseDocument(createResult.getKey())), + new DocumentReplaceOptions().silent(true)); + assertThat(info).isNotNull(); + assertThat(info.getDocuments()).isEmpty(); + assertThat(info.getDocumentsAndErrors()).isEmpty(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteDocument(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + collection.deleteDocument(createResult.getKey()); + final BaseDocument document = collection.getDocument(createResult.getKey(), BaseDocument.class, null); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteDocumentReturnOld(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + final DocumentDeleteOptions options = new DocumentDeleteOptions().returnOld(true); + final DocumentDeleteEntity deleteResult = collection.deleteDocument(createResult.getKey(), + options, BaseDocument.class); + 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("cols") + void deleteDocumentIfMatch(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc, null); + final DocumentDeleteOptions options = new DocumentDeleteOptions().ifMatch(createResult.getRev()); + collection.deleteDocument(createResult.getKey(), options); + final BaseDocument document = collection.getDocument(createResult.getKey(), BaseDocument.class, null); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteDocumentIfMatchFail(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = collection.insertDocument(doc); + final DocumentDeleteOptions options = new DocumentDeleteOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> collection.deleteDocument(createResult.getKey(), options)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteDocumentSilent(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()); + final DocumentDeleteEntity meta = collection.deleteDocument(createResult.getKey(), + new DocumentDeleteOptions().silent(true), BaseDocument.class); + assertThat(meta).isNotNull(); + assertThat(meta.getId()).isNull(); + assertThat(meta.getKey()).isNull(); + assertThat(meta.getRev()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteDocumentsSilent(ArangoCollection collection) { + assumeTrue(isSingleServer()); + final DocumentCreateEntity createResult = collection.insertDocument(new BaseDocument()); + final MultiDocumentEntity> info = collection.deleteDocuments( + Collections.singletonList(createResult.getKey()), + new DocumentDeleteOptions().silent(true), + BaseDocument.class); + assertThat(info).isNotNull(); + assertThat(info.getDocuments()).isEmpty(); + assertThat(info.getDocumentsAndErrors()).isEmpty(); + assertThat(info.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getIndex(ArangoCollection collection) { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = collection.ensureHashIndex(fields, null); + final IndexEntity readResult = collection.getIndex(createResult.getId()); + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getType()).isEqualTo(createResult.getType()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getIndexByKey(ArangoCollection collection) { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = collection.ensureHashIndex(fields, null); + final IndexEntity readResult = collection.getIndex(createResult.getId().split("/")[1]); + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getType()).isEqualTo(createResult.getType()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteIndex(ArangoCollection collection) { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = collection.ensureHashIndex(fields, null); + final String id = collection.deleteIndex(createResult.getId()); + assertThat(id).isEqualTo(createResult.getId()); + Throwable thrown = catchThrowable(() -> collection.db().getIndex(id)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteIndexByKey(ArangoCollection collection) { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = collection.ensureHashIndex(fields, null); + final String id = collection.deleteIndex(createResult.getId().split("/")[1]); + assertThat(id).isEqualTo(createResult.getId()); + Throwable thrown = catchThrowable(() -> collection.db().getIndex(id)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void createHashIndex(ArangoCollection collection) { + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensureHashIndex(fields, null); + 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(); + if (isSingleServer()) { + assertThat(indexResult.getSelectivityEstimate()).isPositive(); + } + assertThat(indexResult.getSparse()).isFalse(); + assertThat(indexResult.getType()).isEqualTo(IndexType.hash); + assertThat(indexResult.getUnique()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void createHashIndexWithOptions(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "hashIndex-" + rnd(); + final HashIndexOptions options = new HashIndexOptions(); + options.name(name); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensureHashIndex(fields, options); + 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(); + if (isSingleServer()) { + assertThat(indexResult.getSelectivityEstimate()).isPositive(); + } + assertThat(indexResult.getSparse()).isFalse(); + assertThat(indexResult.getType()).isEqualTo(IndexType.hash); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getName()).isEqualTo(name); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void createGeoIndex(ArangoCollection collection) { + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + final IndexEntity indexResult = collection.ensureGeoIndex(fields, null); + 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("cols") + void createGeoIndexWithOptions(ArangoCollection collection) { + 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); + 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("cols") + void createGeoIndexLegacyPolygons(ArangoCollection collection) { + 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); + 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("cols") + void createGeo2Index(ArangoCollection collection) { + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensureGeoIndex(fields, null); + 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("cols") + void createGeo2IndexWithOptions(ArangoCollection collection) { + 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); + 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("cols") + void createSkiplistIndex(ArangoCollection collection) { + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensureSkiplistIndex(fields, null); + 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.skiplist); + assertThat(indexResult.getUnique()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void createSkiplistIndexWithOptions(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 5)); + + String name = "skiplistIndex-" + rnd(); + final SkiplistIndexOptions options = new SkiplistIndexOptions(); + options.name(name); + + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + final IndexEntity indexResult = collection.ensureSkiplistIndex(fields, options); + 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.skiplist); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getName()).isEqualTo(name); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void createPersistentIndex(ArangoCollection collection) { + String f1 = "field-" + rnd(); + String f2 = "field-" + rnd(); + final Collection fields = Arrays.asList(f1, f2); + + final IndexEntity indexResult = collection.ensurePersistentIndex(fields, null); + 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("cols") + void createPersistentIndexCacheEnabled(ArangoCollection collection) { + 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)); + 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("cols") + void createPersistentIndexStoredValues(ArangoCollection collection) { + 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")); + 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("cols") + void createPersistentIndexWithOptions(ArangoCollection collection) { + 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); + 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("cols") + void createZKDIndex(ArangoCollection collection) { + 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); + 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("cols") + void createZKDIndexWithOptions(ArangoCollection collection) { + 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); + 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("cols") + void indexEstimates(ArangoCollection collection) { + 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); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getEstimates()).isTrue(); + assertThat(indexResult.getSelectivityEstimate()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void indexEstimatesFalse(ArangoCollection collection) { + 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); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getEstimates()).isFalse(); + assertThat(indexResult.getSelectivityEstimate()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void indexDeduplicate(ArangoCollection collection) { + 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); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getDeduplicate()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void indexDeduplicateFalse(ArangoCollection collection) { + 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); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getDeduplicate()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void createFulltextIndex(ArangoCollection collection) { + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + final IndexEntity indexResult = collection.ensureFulltextIndex(fields, null); + 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("cols") + void createFulltextIndexWithOptions(ArangoCollection collection) { + 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); + 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("cols") + void createTtlIndexWithoutOptions(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 5)); + final Collection fields = new ArrayList<>(); + fields.add("a"); + + Throwable thrown = catchThrowable(() -> collection.ensureTtlIndex(fields, null)); + 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("cols") + void createTtlIndexWithOptions(ArangoCollection collection) { + 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); + 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("cols") + void getIndexes(ArangoCollection collection) { + String f1 = "field-" + rnd(); + final Collection fields = Collections.singletonList(f1); + collection.ensureHashIndex(fields, null); + long matchingIndexes = + collection.getIndexes().stream().filter(i -> i.getType() == IndexType.hash).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("cols") + void exists(ArangoCollection collection) { + assertThat(collection.exists()).isTrue(); + assertThat(collection.db().collection(COLLECTION_NAME + "no").exists()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void truncate(ArangoCollection collection) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + collection.insertDocument(doc, null); + final BaseDocument readResult = collection.getDocument(doc.getKey(), BaseDocument.class, null); + assertThat(readResult.getKey()).isEqualTo(doc.getKey()); + final CollectionEntity truncateResult = collection.truncate(); + assertThat(truncateResult).isNotNull(); + assertThat(truncateResult.getId()).isNotNull(); + final BaseDocument document = collection.getDocument(doc.getKey(), BaseDocument.class, null); + assertThat(document).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getCount(ArangoCollection collection) { + Long initialCount = collection.count().getCount(); + collection.insertDocument(RawJson.of("{}")); + final CollectionPropertiesEntity count = collection.count(); + assertThat(count.getCount()).isEqualTo(initialCount + 1L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void documentExists(ArangoCollection collection) { + final Boolean existsNot = collection.documentExists(rnd(), null); + assertThat(existsNot).isFalse(); + + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + collection.insertDocument(rawJson); + final Boolean exists = collection.documentExists(key, null); + assertThat(exists).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void documentExistsIfMatch(ArangoCollection collection) { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + final DocumentCreateEntity createResult = collection.insertDocument(rawJson); + final DocumentExistsOptions options = new DocumentExistsOptions().ifMatch(createResult.getRev()); + final Boolean exists = collection.documentExists(key, options); + assertThat(exists).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void documentExistsIfMatchFail(ArangoCollection collection) { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + collection.insertDocument(rawJson); + final DocumentExistsOptions options = new DocumentExistsOptions().ifMatch("no"); + final Boolean exists = collection.documentExists(key, options); + assertThat(exists).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void documentExistsIfNoneMatch(ArangoCollection collection) { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + collection.insertDocument(rawJson); + final DocumentExistsOptions options = new DocumentExistsOptions().ifNoneMatch("no"); + final Boolean exists = collection.documentExists(key, options); + assertThat(exists).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void documentExistsIfNoneMatchFail(ArangoCollection collection) { + String key = rnd(); + RawJson rawJson = RawJson.of("{\"_key\":\"" + key + "\"}"); + final DocumentCreateEntity createResult = collection.insertDocument(rawJson); + final DocumentExistsOptions options = new DocumentExistsOptions().ifNoneMatch(createResult.getRev()); + final Boolean exists = collection.documentExists(key, options); + assertThat(exists).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocuments(ArangoCollection collection) { + final Collection values = Arrays.asList(new BaseDocument(), new BaseDocument(), + new BaseDocument()); + + final MultiDocumentEntity docs = collection.insertDocuments(values); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentsOverwriteModeUpdate(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 7)); + + final BaseDocument doc1 = new BaseDocument(UUID.randomUUID().toString()); + doc1.addAttribute("foo", "a"); + final DocumentCreateEntity meta1 = collection.insertDocument(doc1); + + final BaseDocument doc2 = new BaseDocument(UUID.randomUUID().toString()); + doc2.addAttribute("foo", "a"); + final DocumentCreateEntity meta2 = collection.insertDocument(doc2); + + doc1.addAttribute("bar", "b"); + doc2.addAttribute("bar", "b"); + + final MultiDocumentEntity> repsert = + collection.insertDocuments(Arrays.asList(doc1, doc2), + new DocumentCreateOptions().overwriteMode(OverwriteMode.update).returnNew(true)); + 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("cols") + void insertDocumentsJson(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + values.add(RawJson.of("{}")); + values.add(RawJson.of("{}")); + values.add(RawJson.of("{}")); + final MultiDocumentEntity docs = collection.insertDocuments(values); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentsRawData(ArangoCollection collection) { + final RawData values = RawData.of("[{},{},{}]"); + final MultiDocumentEntity docs = collection.insertDocuments(values); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentsRawDataReturnNew(ArangoCollection collection) { + final RawData values = RawData.of("[{\"aaa\":33},{\"aaa\":33},{\"aaa\":33}]"); + final MultiDocumentEntity> docs = + collection.insertDocuments(values, new DocumentCreateOptions().returnNew(true)); + 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).getValue()); + assertThat(jn.has("aaa")).isTrue(); + assertThat(jn.get("aaa").intValue()).isEqualTo(33); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentsOne(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + final MultiDocumentEntity docs = collection.insertDocuments(values); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(1); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentsEmpty(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + final MultiDocumentEntity docs = collection.insertDocuments(values); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).isEmpty(); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertDocumentsReturnNew(ArangoCollection collection) { + 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); + 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("cols") + void insertDocumentsFail(ArangoCollection collection) { + 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); + 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("cols") + void importDocuments(ArangoCollection collection) { + final Collection values = Arrays.asList(new BaseDocument(), new BaseDocument(), + new BaseDocument()); + + final DocumentImportEntity docs = collection.importDocuments(values); + 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("cols") + void importDocumentsJsonList(ArangoCollection collection) { + final Collection values = Arrays.asList( + RawJson.of("{}"), + RawJson.of("{}"), + RawJson.of("{}") + ); + + final DocumentImportEntity docs = collection.importDocuments(values); + 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("cols") + void importDocumentsDuplicateDefaultError(ArangoCollection collection) { + 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); + 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("cols") + void importDocumentsDuplicateError(ArangoCollection collection) { + 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)); + 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("cols") + void importDocumentsDuplicateIgnore(ArangoCollection collection) { + 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)); + 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("cols") + void importDocumentsDuplicateReplace(ArangoCollection collection) { + 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)); + 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("cols") + void importDocumentsDuplicateUpdate(ArangoCollection collection) { + 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)); + 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("cols") + void importDocumentsCompleteFail(ArangoCollection 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))); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getErrorNum()).isEqualTo(1210); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void importDocumentsDetails(ArangoCollection collection) { + 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)); + 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("cols") + void importDocumentsOverwriteFalse(ArangoCollection collection) { + collection.insertDocument(new BaseDocument()); + Long initialCount = collection.count().getCount(); + + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + collection.importDocuments(values, new DocumentImportOptions().overwrite(false)); + assertThat(collection.count().getCount()).isEqualTo(initialCount + 2L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void importDocumentsOverwriteTrue(ArangoCollection collection) { + collection.insertDocument(new BaseDocument()); + + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + collection.importDocuments(values, new DocumentImportOptions().overwrite(true)); + assertThat(collection.count().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("cols") + void importDocumentsJson(ArangoCollection collection) throws JsonProcessingException { + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", rnd()), + Collections.singletonMap("_key", rnd()))); + + final DocumentImportEntity docs = collection.importDocuments(RawData.of(values)); + 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("cols") + void importDocumentsJsonDuplicateDefaultError(ArangoCollection collection) throws JsonProcessingException { + 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(RawData.of(values)); + 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("cols") + void importDocumentsJsonDuplicateError(ArangoCollection collection) throws JsonProcessingException { + 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(RawData.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.error)); + 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("cols") + void importDocumentsJsonDuplicateIgnore(ArangoCollection collection) throws JsonProcessingException { + 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(RawData.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.ignore)); + 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("cols") + void importDocumentsJsonDuplicateReplace(ArangoCollection collection) throws JsonProcessingException { + 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(RawData.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.replace)); + 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("cols") + void importDocumentsJsonDuplicateUpdate(ArangoCollection collection) throws JsonProcessingException { + 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(RawData.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.update)); + 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("cols") + void importDocumentsJsonCompleteFail(ArangoCollection collection) { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + Throwable thrown = catchThrowable(() -> collection.importDocuments(RawData.of(values), + new DocumentImportOptions().complete(true))); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getErrorNum()).isEqualTo(1210); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void importDocumentsJsonDetails(ArangoCollection collection) throws JsonProcessingException { + 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(RawData.of(values), +new DocumentImportOptions().details(true)); + 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("cols") + void importDocumentsJsonOverwriteFalse(ArangoCollection collection) throws JsonProcessingException { + collection.insertDocument(new BaseDocument()); + Long initialCount = collection.count().getCount(); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", rnd()), + Collections.singletonMap("_key", rnd()))); + collection.importDocuments(RawData.of(values), new DocumentImportOptions().overwrite(false)); + assertThat(collection.count().getCount()).isEqualTo(initialCount + 2L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void importDocumentsJsonOverwriteTrue(ArangoCollection collection) throws JsonProcessingException { + collection.insertDocument(new BaseDocument()); + + final String values = mapper.writeValueAsString(Arrays.asList(Collections.singletonMap("_key", rnd()), + Collections.singletonMap("_key", rnd()))); + collection.importDocuments(RawData.of(values), new DocumentImportOptions().overwrite(true)); + assertThat(collection.count().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(RawData.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("cols") + void deleteDocumentsByKey(ArangoCollection collection) { + 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); + final Collection keys = new ArrayList<>(); + keys.add("1"); + keys.add("2"); + final MultiDocumentEntity> deleteResult = collection.deleteDocuments(keys); + 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("cols") + void deleteDocumentsRawDataByKeyReturnOld(ArangoCollection collection) { + final RawData values = RawData.of("[{\"_key\":\"1\"},{\"_key\":\"2\"}]"); + collection.insertDocuments(values); + final RawData keys = RawData.of("[\"1\",\"2\"]"); + MultiDocumentEntity> deleteResult = collection.deleteDocuments(keys, + new DocumentDeleteOptions().returnOld(true)); + 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()).getValue()); + assertThat(jn.get("_key").asText()).isEqualTo(i.getKey()); + } + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteDocumentsByDocuments(ArangoCollection collection) { + 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); + MultiDocumentEntity> deleteResult = collection.deleteDocuments(values); + 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("cols") + void deleteDocumentsByKeyOne(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + collection.insertDocuments(values); + final Collection keys = new ArrayList<>(); + keys.add("1"); + final MultiDocumentEntity> deleteResult = collection.deleteDocuments(keys); + 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("cols") + void deleteDocumentsByDocumentOne(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + collection.insertDocuments(values); + final MultiDocumentEntity> deleteResult = collection.deleteDocuments(values); + 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("cols") + void deleteDocumentsEmpty(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + collection.insertDocuments(values); + final Collection keys = new ArrayList<>(); + final MultiDocumentEntity deleteResult = collection.deleteDocuments(keys); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).isEmpty(); + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteDocumentsByKeyNotExisting(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + collection.insertDocuments(values); + final Collection keys = Arrays.asList(rnd(), rnd()); + + final MultiDocumentEntity deleteResult = collection.deleteDocuments(keys); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).isEmpty(); + assertThat(deleteResult.getErrors()).hasSize(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void deleteDocumentsByDocumentsNotExisting(ArangoCollection collection) { + 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); + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments()).isEmpty(); + assertThat(deleteResult.getErrors()).hasSize(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocuments(ArangoCollection collection) { + final Collection values = Arrays.asList(new BaseDocument(rnd()), new BaseDocument(rnd())); + collection.insertDocuments(values); + values.forEach(it -> it.addAttribute("a", "test")); + + final MultiDocumentEntity updateResult = collection.updateDocuments(values); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentsWithDifferentReturnType(ArangoCollection collection) { + 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); + + 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); + 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("cols") + void updateDocumentsOne(ArangoCollection collection) { + 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); + assertThat(updateResult.getDocuments()).hasSize(1); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentsEmpty(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + final MultiDocumentEntity updateResult = collection.updateDocuments(values); + assertThat(updateResult.getDocuments()).isEmpty(); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentsWithoutKey(ArangoCollection collection) { + 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); + assertThat(updateResult.getDocuments()).hasSize(1); + assertThat(updateResult.getErrors()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentsJson(ArangoCollection collection) { + 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); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentsRawData(ArangoCollection collection) { + final RawData values = RawData.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values); + + final RawData updatedValues = RawData.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + + "\"foo\":\"bar\"}]"); + final MultiDocumentEntity updateResult = collection.updateDocuments(updatedValues); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void updateDocumentsRawDataReturnNew(ArangoCollection collection) { + final RawData values = RawData.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values); + + final RawData updatedValues = RawData.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + + "\"foo\":\"bar\"}]"); + MultiDocumentEntity> updateResult = + collection.updateDocuments(updatedValues, new DocumentUpdateOptions().returnNew(true)); + 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).getValue()); + assertThat(jn.has("foo")).isTrue(); + assertThat(jn.get("foo").textValue()).isEqualTo("bar"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocuments(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + { + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + } + collection.insertDocuments(values); + final Collection updatedValues = new ArrayList<>(); + for (final BaseDocument i : values) { + i.addAttribute("a", "test"); + updatedValues.add(i); + } + final MultiDocumentEntity updateResult = collection.replaceDocuments(updatedValues); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentsOne(ArangoCollection collection) { + 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); + assertThat(updateResult.getDocuments()).hasSize(1); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentsEmpty(ArangoCollection collection) { + final Collection values = new ArrayList<>(); + final MultiDocumentEntity updateResult = collection.updateDocuments(values); + assertThat(updateResult.getDocuments()).isEmpty(); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentsWithoutKey(ArangoCollection collection) { + 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); + assertThat(updateResult.getDocuments()).hasSize(1); + assertThat(updateResult.getErrors()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentsJson(ArangoCollection collection) { + 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.replaceDocuments(updatedValues); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentsRawData(ArangoCollection collection) { + final RawData values = RawData.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values); + + final RawData updatedValues = RawData.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + + "\"foo\":\"bar\"}]"); + final MultiDocumentEntity updateResult = collection.replaceDocuments(updatedValues); + assertThat(updateResult.getDocuments()).hasSize(2); + assertThat(updateResult.getErrors()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void replaceDocumentsRawDataReturnNew(ArangoCollection collection) { + final RawData values = RawData.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values); + + final RawData updatedValues = RawData.of("[{\"_key\":\"1\", \"foo\":\"bar\"}, {\"_key\":\"2\", " + + "\"foo\":\"bar\"}]"); + MultiDocumentEntity> updateResult = + collection.replaceDocuments(updatedValues, new DocumentReplaceOptions().returnNew(true)); + 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).getValue()); + assertThat(jn.has("foo")).isTrue(); + assertThat(jn.get("foo").textValue()).isEqualTo("bar"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getInfo(ArangoCollection collection) { + final CollectionEntity result = collection.getInfo(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getPropeties(ArangoCollection collection) { + final CollectionPropertiesEntity result = collection.getProperties(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME); + assertThat(result.getCount()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void changeProperties(ArangoCollection collection) { + final CollectionPropertiesEntity properties = collection.getProperties(); + 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); + 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())); + if (isAtLeastVersion(3, 7)) { + assertThat(revertedProperties.getSchema()).isNull(); + } + + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void rename(ArangoCollection collection) { + assumeTrue(isSingleServer()); + ArangoDatabase db = collection.db(); + final CollectionEntity result = collection.rename(COLLECTION_NAME + "1"); + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME + "1"); + final CollectionEntity info = db.collection(COLLECTION_NAME + "1").getInfo(); + assertThat(info.getName()).isEqualTo(COLLECTION_NAME + "1"); + Throwable thrown = catchThrowable(() -> db.collection(COLLECTION_NAME).getInfo()); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(404); + db.collection(COLLECTION_NAME + "1").rename(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void responsibleShard(ArangoCollection collection) { + assumeTrue(isCluster()); + assumeTrue(isAtLeastVersion(3, 5)); + ShardEntity shard = collection.getResponsibleShard(new BaseDocument("testKey")); + assertThat(shard).isNotNull(); + assertThat(shard.getShardId()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void renameDontBreaksCollectionHandler(ArangoCollection collection) { + assumeTrue(isSingleServer()); + collection.rename(COLLECTION_NAME + "1"); + assertThat(collection.getInfo()).isNotNull(); + collection.db().collection(COLLECTION_NAME + "1").rename(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getRevision(ArangoCollection collection) { + final CollectionRevisionEntity result = collection.getRevision(); + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME); + assertThat(result.getRevision()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void keyWithSpecialCharacter(ArangoCollection collection) { + final String key = "myKey_-:.@()+,=;$!*'%-" + UUID.randomUUID(); + collection.insertDocument(new BaseDocument(key)); + final BaseDocument doc = collection.getDocument(key, BaseDocument.class); + assertThat(doc).isNotNull(); + assertThat(doc.getKey()).isEqualTo(key); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void alreadyUrlEncodedkey(ArangoCollection collection) { + final String key = "http%3A%2F%2Fexample.com%2F-" + UUID.randomUUID(); + collection.insertDocument(new BaseDocument(key)); + final BaseDocument doc = collection.getDocument(key, BaseDocument.class); + assertThat(doc).isNotNull(); + assertThat(doc.getKey()).isEqualTo(key); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void grantAccessRW(ArangoCollection collection) { + ArangoDB arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null); + collection.grantAccess("user1", Permissions.RW); + } finally { + arangoDB.deleteUser("user1"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void grantAccessRO(ArangoCollection collection) { + ArangoDB arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null); + collection.grantAccess("user1", Permissions.RO); + } finally { + arangoDB.deleteUser("user1"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void grantAccessNONE(ArangoCollection collection) { + ArangoDB arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null); + collection.grantAccess("user1", Permissions.NONE); + } finally { + arangoDB.deleteUser("user1"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void grantAccessUserNotFound(ArangoCollection collection) { + Throwable thrown = catchThrowable(() -> collection.grantAccess("user1", Permissions.RW)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void revokeAccess(ArangoCollection collection) { + ArangoDB arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null); + collection.grantAccess("user1", Permissions.NONE); + } finally { + arangoDB.deleteUser("user1"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void revokeAccessUserNotFound(ArangoCollection collection) { + Throwable thrown = catchThrowable(() -> collection.grantAccess("user1", Permissions.NONE)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void resetAccess(ArangoCollection collection) { + ArangoDB arangoDB = collection.db().arango(); + try { + arangoDB.createUser("user1", "1234", null); + collection.resetAccess("user1"); + } finally { + arangoDB.deleteUser("user1"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void resetAccessUserNotFound(ArangoCollection collection) { + Throwable thrown = catchThrowable(() -> collection.resetAccess("user1")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getPermissions(ArangoCollection collection) { + assertThat(collection.getPermissions("root")).isEqualTo(Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void annotationsInParamsAndMethods(ArangoCollection collection) { + assumeTrue(collection.getSerde().getUserSerde() instanceof JacksonSerde, "JacksonSerde only"); + AnnotatedEntity entity = new AnnotatedEntity(UUID.randomUUID().toString()); + AnnotatedEntity doc = collection.insertDocument(entity, new DocumentCreateOptions().returnNew(true)).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; + } + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoCursorTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoCursorTest.java new file mode 100644 index 000000000..cfbd98775 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoCursorTest.java @@ -0,0 +1,133 @@ +/* + * 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.model.AqlQueryOptions; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoCursorTest extends BaseJunit5 { + + @BeforeAll + static void init() { + initDB(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void firstStream(ArangoDatabase db) { + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + final Optional first = cursor.stream().findFirst(); + assertThat(first).isPresent(); + assertThat(first.get().isInt()).isTrue(); + assertThat(first.get().asLong()).isZero(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void next(ArangoDatabase db) { + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", new AqlQueryOptions().batchSize(5), + JsonNode.class); + while (cursor.hasNext()) { + cursor.next(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void mapFilterCountStream(ArangoDatabase db) { + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + final long count = cursor.stream().map(JsonNode::asLong).filter(t -> t < 50).count(); + assertThat(count).isEqualTo(50L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void mapFilterCollectIntoSetStream(ArangoDatabase db) { + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + final Set target = cursor.stream().map(JsonNode::asLong).filter(t -> t < 50).collect(Collectors.toSet()); + assertThat(target) + .isNotNull() + .hasSize(50); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void forEach(ArangoDatabase db) { + final AtomicLong i = new AtomicLong(0L); + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + cursor.forEach(t -> assertThat(t.asLong()).isEqualTo(i.getAndIncrement())); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void mapForeachStream(ArangoDatabase db) { + final AtomicLong i = new AtomicLong(0L); + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + cursor.stream().map(JsonNode::asLong).forEach(t -> assertThat(t).isEqualTo(i.getAndIncrement())); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void mapFilterForEachStream(ArangoDatabase db) { + final AtomicLong i = new AtomicLong(0L); + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + cursor.stream().map(JsonNode::asLong).filter(t -> t < 50).forEach(t -> assertThat(t).isEqualTo(i.getAndIncrement())); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void anyMatchStream(ArangoDatabase db) { + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + final boolean match = cursor.stream().anyMatch(t -> t.asLong() == 50L); + assertThat(match).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void noneMatchStream(ArangoDatabase db) { + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + final boolean match = cursor.stream().noneMatch(t -> t.asLong() == 100L); + assertThat(match).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void allMatchStream(ArangoDatabase db) { + final ArangoCursor cursor = db.query("FOR i IN 0..99 RETURN i", JsonNode.class); + final boolean match = cursor.stream().allMatch(t -> t.asLong() < 100L); + assertThat(match).isTrue(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDBTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDBTest.java new file mode 100644 index 000000000..0bf363642 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDBTest.java @@ -0,0 +1,656 @@ +/* + * 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.config.FileConfigPropertiesProvider; +import com.arangodb.internal.serde.SerdeUtils; +import com.arangodb.model.*; +import com.arangodb.model.LogOptions.SortOrder; +import com.arangodb.shaded.fasterxml.jackson.databind.JsonNode; +import com.arangodb.util.RawJson; +import com.arangodb.util.TestUtils; +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.concurrent.Executors; +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 Mark Vollmary + * @author ReÅŸat SABIQ + * @author Michele Rastelli + */ +class ArangoDBTest extends BaseJunit5 { + + private static final DbName DB1 = DbName.of("ArangoDBTest_db1"); + private static final DbName DB2 = DbName.of("ArangoDBTest_db2"); + + private static final String ROOT = "root"; + private static final String PW = "machts der hund"; + + private static Boolean extendedNames; + + @BeforeAll + static void initDBs() { + initDB(DB1); + initDB(DB2); + } + + @AfterAll + static void shutdown() { + dropDB(DB1); + dropDB(DB2); + } + + private boolean supportsExtendedNames(ArangoDB arangoDB) { + if (extendedNames == null) { + try { + ArangoDatabase testDb = arangoDB.db(DbName.of("test-" + TestUtils.generateRandomDbName(20, true))); + testDb.create(); + extendedNames = true; + testDb.drop(); + } catch (ArangoDBException e) { + extendedNames = false; + } + } + return extendedNames; + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getVersion(ArangoDB arangoDB) { + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version.getServer()).isNotNull(); + assertThat(version.getVersion()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createAndDeleteDatabase(ArangoDB arangoDB) { + final DbName dbName = DbName.of("testDB-" + TestUtils.generateRandomDbName(20, + supportsExtendedNames(arangoDB))); + final Boolean resultCreate; + resultCreate = arangoDB.createDatabase(dbName); + assertThat(resultCreate).isTrue(); + final Boolean resultDelete = arangoDB.db(dbName).drop(); + assertThat(resultDelete).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createWithNotNormalizedName(ArangoDB arangoDB) { + assumeTrue(supportsExtendedNames(arangoDB)); + + final String dbName = "testDB-\u006E\u0303\u00f1"; + DbName normalized = DbName.normalize(dbName); + arangoDB.createDatabase(normalized); + arangoDB.db(normalized).drop(); + + Throwable thrown = catchThrowable(() -> DbName.of(dbName)); + assertThat(thrown) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("not normalized"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createDatabaseWithOptions(ArangoDB arangoDB) { + assumeTrue(isCluster()); + assumeTrue(isAtLeastVersion(3, 6)); + final DbName dbName = DbName.of("testDB-" + TestUtils.generateRandomDbName(20, + supportsExtendedNames(arangoDB))); + final Boolean resultCreate = arangoDB.createDatabase(new DBCreateOptions() + .name(dbName) + .options(new DatabaseOptions() + .writeConcern(2) + .replicationFactor(2) + .sharding("") + ) + ); + assertThat(resultCreate).isTrue(); + + DatabaseEntity info = arangoDB.db(dbName).getInfo(); + assertThat(info.getReplicationFactor().getValue()).isEqualTo(2); + assertThat(info.getWriteConcern()).isEqualTo(2); + assertThat(info.getSharding()).isEmpty(); + + final Boolean resultDelete = arangoDB.db(dbName).drop(); + assertThat(resultDelete).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createDatabaseWithOptionsSatellite(ArangoDB arangoDB) { + assumeTrue(isCluster()); + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 6)); + + final DbName dbName = DbName.of("testDB-" + TestUtils.generateRandomDbName(20, + supportsExtendedNames(arangoDB))); + final Boolean resultCreate = arangoDB.createDatabase(new DBCreateOptions() + .name(dbName) + .options(new DatabaseOptions() + .writeConcern(2) + .replicationFactor(ReplicationFactor.ofSatellite()) + .sharding("") + ) + ); + assertThat(resultCreate).isTrue(); + + DatabaseEntity info = arangoDB.db(dbName).getInfo(); + assertThat(info.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + assertThat(info.getWriteConcern()).isEqualTo(2); + assertThat(info.getSharding()).isEmpty(); + + final Boolean resultDelete = arangoDB.db(dbName).drop(); + assertThat(resultDelete).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createDatabaseWithUsers(ArangoDB arangoDB) throws InterruptedException { + final DbName dbName = DbName.of("testDB-" + TestUtils.generateRandomDbName(20, + supportsExtendedNames(arangoDB))); + final Map extra = Collections.singletonMap("key", "value"); + final Boolean resultCreate = arangoDB.createDatabase(new DBCreateOptions() + .name(dbName) + .users(Collections.singletonList(new DatabaseUsersOptions() + .active(true) + .username("testUser") + .passwd("testPasswd") + .extra(extra) + )) + ); + assertThat(resultCreate).isTrue(); + + DatabaseEntity info = arangoDB.db(dbName).getInfo(); + assertThat(info.getName()).isEqualTo(dbName.get()); + + Optional retrievedUserOptional = arangoDB.getUsers().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(new FileConfigPropertiesProvider()) + .user("testUser") + .password("testPasswd") + .build(); + + // check if testUser has been created and can access the created db + ArangoCollection collection = arangoDBTestUser.db(dbName).collection("col-" + UUID.randomUUID()); + collection.create(); + arangoDBTestUser.shutdown(); + + final Boolean resultDelete = arangoDB.db(dbName).drop(); + assertThat(resultDelete).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getDatabases(ArangoDB arangoDB) { + Collection dbs = arangoDB.getDatabases(); + assertThat(dbs).contains("_system", DB1.get()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getAccessibleDatabases(ArangoDB arangoDB) { + final Collection dbs = arangoDB.getAccessibleDatabases(); + assertThat(dbs).contains("_system"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getAccessibleDatabasesFor(ArangoDB arangoDB) { + final Collection dbs = arangoDB.getAccessibleDatabasesFor("root"); + assertThat(dbs).contains("_system"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void createUser(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + final UserEntity result = arangoDB.createUser(username, PW, null); + assertThat(result.getUser()).isEqualTo(username); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void deleteUser(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + arangoDB.createUser(username, PW, null); + arangoDB.deleteUser(username); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getUserRoot(ArangoDB arangoDB) { + final UserEntity user = arangoDB.getUser(ROOT); + assertThat(user.getUser()).isEqualTo(ROOT); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getUser(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + arangoDB.createUser(username, PW, null); + final UserEntity user = arangoDB.getUser(username); + assertThat(user.getUser()).isEqualTo(username); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getUsersOnlyRoot(ArangoDB arangoDB) { + final Collection users = arangoDB.getUsers(); + assertThat(users).isNotEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getUsers(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + // Allow & account for pre-existing users other than ROOT: + final Collection initialUsers = arangoDB.getUsers(); + + arangoDB.createUser(username, PW, null); + final Collection users = arangoDB.getUsers(); + 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.createUser(username, PW, null); + arangoDB.updateUser(username, null); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void updateUser(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + final Map extra = new HashMap<>(); + extra.put("hund", false); + arangoDB.createUser(username, PW, new UserCreateOptions().extra(extra)); + extra.put("hund", true); + extra.put("mund", true); + final UserEntity user = arangoDB.updateUser(username, new UserUpdateOptions().extra(extra)); + assertThat(user.getExtra()).hasSize(2); + assertThat(user.getExtra()).containsKey("hund"); + assertThat(Boolean.valueOf(String.valueOf(user.getExtra().get("hund")))).isTrue(); + final UserEntity user2 = arangoDB.getUser(username); + 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) { + String username = "user-" + UUID.randomUUID(); + final Map extra = new HashMap<>(); + extra.put("hund", false); + arangoDB.createUser(username, PW, new UserCreateOptions().extra(extra)); + extra.remove("hund"); + extra.put("mund", true); + final UserEntity user = arangoDB.replaceUser(username, new UserUpdateOptions().extra(extra)); + assertThat(user.getExtra()).hasSize(1); + assertThat(user.getExtra()).containsKey("mund"); + assertThat(Boolean.valueOf(String.valueOf(user.getExtra().get("mund")))).isTrue(); + final UserEntity user2 = arangoDB.getUser(username); + 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.createUser(username, PW); + arangoDB.grantDefaultDatabaseAccess(username, Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void updateUserDefaultCollectionAccess(ArangoDB arangoDB) { + String username = "user-" + UUID.randomUUID(); + arangoDB.createUser(username, PW); + arangoDB.grantDefaultCollectionAccess(username, Permissions.RW); + } + + @Test + void authenticationFailPassword() { + final ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .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() { + final ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .user("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 executeGetVersion(ArangoDB arangoDB) { + Request request = Request.builder() + .db(DbName.SYSTEM) + .method(Request.Method.GET) + .path("/_api/version") + .queryParam("details", "true") + .build(); + final Response response = arangoDB.execute(request, RawJson.class); + JsonNode body = SerdeUtils.INSTANCE.parseJson(response.getBody().getValue()); + 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) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.getLogEntries(null); + assertThat(logs.getTotal()).isPositive(); + assertThat(logs.getMessages()).hasSize(logs.getTotal().intValue()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesUpto(ArangoDB arangoDB) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logsUpto = arangoDB.getLogEntries(new LogOptions().upto(LogLevel.WARNING)); + assertThat(logsUpto.getMessages()) + .map(LogEntriesEntity.Message::getLevel) + .doesNotContain("INFO"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesLevel(ArangoDB arangoDB) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logsInfo = arangoDB.getLogEntries(new LogOptions().level(LogLevel.INFO)); + assertThat(logsInfo.getMessages()) + .map(LogEntriesEntity.Message::getLevel) + .containsOnly("INFO"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesStart(ArangoDB arangoDB) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.getLogEntries(null); + final Long firstId = logs.getMessages().get(0).getId(); + final LogEntriesEntity logsStart = arangoDB.getLogEntries(new LogOptions().start(firstId + 1)); + assertThat(logsStart.getMessages()) + .map(LogEntriesEntity.Message::getId) + .doesNotContain(firstId); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesSize(ArangoDB arangoDB) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.getLogEntries(null); + int count = logs.getMessages().size(); + assertThat(count).isPositive(); + final LogEntriesEntity logsSize = arangoDB.getLogEntries(new LogOptions().size(count - 1)); + assertThat(logsSize.getMessages()).hasSize(count - 1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesOffset(ArangoDB arangoDB) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.getLogEntries(null); + assertThat(logs.getTotal()).isPositive(); + Long firstId = logs.getMessages().get(0).getId(); + final LogEntriesEntity logsOffset = arangoDB.getLogEntries(new LogOptions().offset(1)); + assertThat(logsOffset.getMessages()) + .map(LogEntriesEntity.Message::getId) + .doesNotContain(firstId); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getLogEntriesSearch(ArangoDB arangoDB) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.getLogEntries(null); + 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) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.getLogEntries(new LogOptions().sort(SortOrder.asc)); + 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) { + assumeTrue(isAtLeastVersion(3, 8)); + final LogEntriesEntity logs = arangoDB.getLogEntries(new LogOptions().sort(SortOrder.desc)); + 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) { + assumeTrue(isAtLeastVersion(3, 7)); // it fails in 3.6 active-failover (BTS-362) + final LogLevelEntity logLevel = arangoDB.getLogLevel(); + assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.INFO); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void setLogLevel(ArangoDB arangoDB) { + 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.setLogLevel(entity); + assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + } finally { + entity.setAgency(LogLevelEntity.LogLevel.INFO); + arangoDB.setLogLevel(entity); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void setAllLogLevel(ArangoDB arangoDB) { + assumeTrue(isAtLeastVersion(3, 9)); + final LogLevelEntity entity = new LogLevelEntity(); + try { + entity.setAll(LogLevelEntity.LogLevel.ERROR); + final LogLevelEntity logLevel = arangoDB.setLogLevel(entity); + assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + assertThat(logLevel.getQueries()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + LogLevelEntity retrievedLevels = arangoDB.getLogLevel(); + assertThat(retrievedLevels.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + } finally { + entity.setAll(LogLevelEntity.LogLevel.INFO); + arangoDB.setLogLevel(entity); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void getQueryOptimizerRules(ArangoDB arangoDB) { + assumeTrue(isAtLeastVersion(3, 10)); + final Collection rules = arangoDB.getQueryOptimizerRules(); + 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.db(DbName.of("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(new FileConfigPropertiesProvider()) + .host("not-accessible", 8529).host("127.0.0.1", 8529).build(); + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void loadproperties() { + Throwable thrown = catchThrowable(() -> new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider("arangodb", "/arangodb-bad.properties")) + ); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void accessMultipleDatabases(ArangoDB arangoDB) { + final ArangoDBVersion version1 = arangoDB.db(DB1).getVersion(); + assertThat(version1).isNotNull(); + final ArangoDBVersion version2 = arangoDB.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.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).isPositive(); + } else { + assertThat(avg).isEqualTo(0.0); + assertThat(values).isEmpty(); + } + + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDatabaseTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDatabaseTest.java new file mode 100644 index 000000000..bfd9924cd --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDatabaseTest.java @@ -0,0 +1,1413 @@ +/* + * 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.RawBytes; +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.io.IOException; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoDatabaseTest 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("dbs") + void getVersion(ArangoDatabase db) { + final ArangoDBVersion version = db.getVersion(); + assertThat(version).isNotNull(); + assertThat(version.getServer()).isNotNull(); + assertThat(version.getVersion()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getEngine(ArangoDatabase db) { + final ArangoDBEngine engine = db.getEngine(); + assertThat(engine).isNotNull(); + assertThat(engine.getName()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void exists(ArangoDB arangoDB) { + assertThat(arangoDB.db(TEST_DB).exists()).isTrue(); + assertThat(arangoDB.db(DbName.of("no")).exists()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getAccessibleDatabases(ArangoDatabase db) { + final Collection dbs = db.getAccessibleDatabases(); + assertThat(dbs).contains("_system"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollection(ArangoDatabase db) { + String name = "collection-" + rnd(); + final CollectionEntity result = db.createCollection(name, null); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithReplicationFactor(ArangoDatabase db) { + assumeTrue(isCluster()); + String name = "collection-" + rnd(); + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions().replicationFactor(2)); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + CollectionPropertiesEntity props = db.collection(name).getProperties(); + assertThat(props.getReplicationFactor().getValue()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithWriteConcern(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isCluster()); + + String name = "collection-" + rnd(); + final CollectionEntity result = db.createCollection(name, + new CollectionCreateOptions().replicationFactor(2).writeConcern(2)); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + CollectionPropertiesEntity props = db.collection(name).getProperties(); + assertThat(props.getReplicationFactor().getValue()).isEqualTo(2); + assertThat(props.getWriteConcern()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createSatelliteCollection(ArangoDatabase db) { + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + + String name = "collection-" + rnd(); + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions().replicationFactor(ReplicationFactor.ofSatellite())); + + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + CollectionPropertiesEntity props = db.collection(name).getProperties(); + assertThat(props.getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithNumberOfShards(ArangoDatabase db) { + assumeTrue(isCluster()); + String name = "collection-" + rnd(); + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions().numberOfShards(2)); + + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + CollectionPropertiesEntity props = db.collection(name).getProperties(); + assertThat(props.getNumberOfShards()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithShardingStrategys(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + assumeTrue(isCluster()); + + String name = "collection-" + rnd(); + final CollectionEntity result = db.createCollection(name, new CollectionCreateOptions() + .shardingStrategy(ShardingStrategy.COMMUNITY_COMPAT.getInternalName())); + + 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("dbs") + void createCollectionWithSmartJoinAttribute(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + + String name = "collection-" + rnd(); + final CollectionEntity result = db.createCollection(name, + new CollectionCreateOptions().smartJoinAttribute("test123").shardKeys("_key:")); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + assertThat(db.collection(name).getProperties().getSmartJoinAttribute()).isEqualTo("test123"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithSmartJoinAttributeWrong(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + + String name = "collection-" + rnd(); + + try { + db.createCollection(name, new CollectionCreateOptions().smartJoinAttribute("test123")); + } catch (ArangoDBException e) { + assertThat(e.getErrorNum()).isEqualTo(4006); + assertThat(e.getResponseCode()).isEqualTo(400); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithNumberOfShardsAndShardKey(ArangoDatabase db) { + assumeTrue(isCluster()); + + String name = "collection-" + rnd(); + final CollectionEntity result = db + .createCollection(name, new CollectionCreateOptions().numberOfShards(2).shardKeys("a")); + 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("dbs") + void createCollectionWithNumberOfShardsAndShardKeys(ArangoDatabase db) { + assumeTrue(isCluster()); + String name = "collection-" + rnd(); + final CollectionEntity result = db.createCollection(name, + new CollectionCreateOptions().numberOfShards(2).shardKeys("a", "b")); + 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("dbs") + void createCollectionWithDistributeShardsLike(ArangoDatabase db) { + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + + final Integer numberOfShards = 3; + + String name1 = "collection-" + rnd(); + String name2 = "collection-" + rnd(); + db.createCollection(name1, new CollectionCreateOptions().numberOfShards(numberOfShards)); + db.createCollection(name2, new CollectionCreateOptions().distributeShardsLike(name1)); + + assertThat(db.collection(name1).getProperties().getNumberOfShards()).isEqualTo(numberOfShards); + assertThat(db.collection(name2).getProperties().getNumberOfShards()).isEqualTo(numberOfShards); + } + + private void createCollectionWithKeyType(ArangoDatabase db, KeyType keyType) { + String name = "collection-" + rnd(); + db.createCollection(name, new CollectionCreateOptions().keyOptions( + false, + keyType, + null, + null + )); + assertThat(db.collection(name).getProperties().getKeyOptions().getType()).isEqualTo(keyType); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithKeyTypeAutoincrement(ArangoDatabase db) { + assumeTrue(isSingleServer()); + createCollectionWithKeyType(db, KeyType.autoincrement); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithKeyTypePadded(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + createCollectionWithKeyType(db, KeyType.padded); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithKeyTypeTraditional(ArangoDatabase db) { + createCollectionWithKeyType(db, KeyType.traditional); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithKeyTypeUuid(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + createCollectionWithKeyType(db, KeyType.uuid); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCollectionWithJsonSchema(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 7)); + String name = "collection-" + rnd(); + 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) + ) + ); + 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("dbs") + void createCollectionWithComputedFields(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + String cName = "collection-" + rnd(); + 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)); + + 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("dbs") + void deleteCollection(ArangoDatabase db) { + String name = "collection-" + rnd(); + db.createCollection(name, null); + db.collection(name).drop(); + Throwable thrown = catchThrowable(() -> db.collection(name).getInfo()); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void deleteSystemCollection(ArangoDatabase db) { + final String name = "_system_test"; + db.createCollection(name, new CollectionCreateOptions().isSystem(true)); + 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("dbs") + void deleteSystemCollectionFail(ArangoDatabase 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("dbs") + void getIndex(ArangoDatabase db) { + final Collection fields = Collections.singletonList("field-" + rnd()); + final IndexEntity createResult = db.collection(CNAME1).ensureHashIndex(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("dbs") + void deleteIndex(ArangoDatabase db) { + final Collection fields = Collections.singletonList("field-" + rnd()); + final IndexEntity createResult = db.collection(CNAME1).ensureHashIndex(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("dbs") + void getCollections(ArangoDatabase db) { + final Collection collections = db.getCollections(null); + long count = collections.stream().map(CollectionEntity::getName).filter(it -> it.equals(CNAME1)).count(); + assertThat(count).isEqualTo(1L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getCollectionsExcludeSystem(ArangoDatabase db) { + final CollectionsReadOptions options = new CollectionsReadOptions().excludeSystem(true); + final Collection nonSystemCollections = db.getCollections(options); + final Collection allCollections = db.getCollections(null); + assertThat(allCollections).hasSizeGreaterThan(nonSystemCollections.size()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void grantAccess(ArangoDB arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).grantAccess(user); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void grantAccessRW(ArangoDB arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).grantAccess(user, Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void grantAccessRO(ArangoDB arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).grantAccess(user, Permissions.RO); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void grantAccessNONE(ArangoDB arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).grantAccess(user, Permissions.NONE); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void grantAccessUserNotFound(ArangoDatabase db) { + String user = "user-" + rnd(); + Throwable thrown = catchThrowable(() -> db.grantAccess(user, Permissions.RW)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void revokeAccess(ArangoDB arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).revokeAccess(user); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void revokeAccessUserNotFound(ArangoDatabase db) { + String user = "user-" + rnd(); + Throwable thrown = catchThrowable(() -> db.revokeAccess(user)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void resetAccess(ArangoDB arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234", null); + arangoDB.db(TEST_DB).resetAccess(user); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void resetAccessUserNotFound(ArangoDatabase db) { + String user = "user-" + rnd(); + Throwable thrown = catchThrowable(() -> db.resetAccess(user)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void grantDefaultCollectionAccess(ArangoDB arangoDB) { + String user = "user-" + rnd(); + arangoDB.createUser(user, "1234"); + arangoDB.db(TEST_DB).grantDefaultCollectionAccess(user, Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getPermissions(ArangoDatabase db) { + assertThat(db.getPermissions("root")).isEqualTo(Permissions.RW); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void query(ArangoDatabase 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", null, null, String.class); + assertThat((Object) cursor).isNotNull(); + for (int i = 0; i < 10; i++, cursor.next()) { + assertThat((Iterator) cursor).hasNext(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithNullBindVar(ArangoDatabase db) { + final ArangoCursor cursor = db.query("return @foo", Collections.singletonMap("foo", null), null, + Object.class); + assertThat(cursor.hasNext()).isTrue(); + assertThat(cursor.next()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryForEach(ArangoDatabase 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", null, null, String.class); + assertThat((Object) cursor).isNotNull(); + + int i = 0; + while (cursor.hasNext()) { + cursor.next(); + i++; + } + assertThat(i).isGreaterThanOrEqualTo(10); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithCount(ArangoDatabase 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", null, new AqlQueryOptions().count(true), + String.class); + 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("dbs") + void queryWithLimitAndFullCount(ArangoDatabase 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", null, new AqlQueryOptions().fullCount(true), + String.class); + assertThat((Object) cursor).isNotNull(); + for (int i = 0; i < 5; i++, cursor.next()) { + assertThat((Iterator) cursor).hasNext(); + } + assertThat(cursor.getStats()).isNotNull(); + assertThat(((Number) cursor.getStats().get("fullCount")).intValue()) + .isGreaterThanOrEqualTo(10); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithBatchSize(ArangoDatabase 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", null, new AqlQueryOptions().batchSize(5).count(true), + String.class); + + assertThat((Object) cursor).isNotNull(); + for (int i = 0; i < 10; i++, cursor.next()) { + assertThat((Iterator) cursor).hasNext(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryIterateWithBatchSize(ArangoDatabase 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", null, new AqlQueryOptions().batchSize(5).count(true), + String.class); + + 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("dbs") + void queryWithTTL(ArangoDatabase 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", null, new AqlQueryOptions().batchSize(5).ttl(ttl), + String.class); + + 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("dbs") + void changeQueryCache(ArangoDatabase db) { + QueryCachePropertiesEntity properties = db.getQueryCacheProperties(); + assertThat(properties).isNotNull(); + assertThat(properties.getMode()).isEqualTo(CacheMode.off); + assertThat(properties.getMaxResults()).isPositive(); + + properties.setMode(CacheMode.on); + properties = db.setQueryCacheProperties(properties); + assertThat(properties).isNotNull(); + assertThat(properties.getMode()).isEqualTo(CacheMode.on); + + properties = db.getQueryCacheProperties(); + assertThat(properties.getMode()).isEqualTo(CacheMode.on); + + final QueryCachePropertiesEntity properties2 = new QueryCachePropertiesEntity(); + properties2.setMode(CacheMode.off); + db.setQueryCacheProperties(properties2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithCache(ArangoDatabase 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", null, + new AqlQueryOptions().cache(true), String.class); + + 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", null, + new AqlQueryOptions().cache(true), String.class); + + assertThat((Object) cachedCursor).isNotNull(); + assertThat(cachedCursor.isCached()).isTrue(); + + final QueryCachePropertiesEntity properties2 = new QueryCachePropertiesEntity(); + properties2.setMode(CacheMode.off); + db.setQueryCacheProperties(properties2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithMemoryLimit(ArangoDatabase db) { + Throwable thrown = catchThrowable(() -> db.query("RETURN 1..100000", null, + new AqlQueryOptions().memoryLimit(32 * 1024L), String.class)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) thrown).getErrorNum()).isEqualTo(32); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithFailOnWarningTrue(ArangoDatabase db) { + Throwable thrown = catchThrowable(() -> db.query("RETURN 1 / 0", null, + new AqlQueryOptions().failOnWarning(true), String.class)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithFailOnWarningFalse(ArangoDatabase db) { + final ArangoCursor cursor = db + .query("RETURN 1 / 0", null, new AqlQueryOptions().failOnWarning(false), String.class); + assertThat(cursor.next()).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithTimeout(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 6)); + Throwable thrown = catchThrowable(() -> db.query("RETURN SLEEP(1)", null, + new AqlQueryOptions().maxRuntime(0.1), String.class).next()); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) thrown).getResponseCode()).isEqualTo(410); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithMaxWarningCount(ArangoDatabase db) { + final ArangoCursor cursorWithWarnings = db + .query("RETURN 1 / 0", null, new AqlQueryOptions(), String.class); + assertThat(cursorWithWarnings.getWarnings()).hasSize(1); + final ArangoCursor cursorWithLimitedWarnings = db + .query("RETURN 1 / 0", null, new AqlQueryOptions().maxWarningCount(0L), String.class); + final Collection warnings = cursorWithLimitedWarnings.getWarnings(); + assertThat(warnings).isNullOrEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryCursor(ArangoDatabase db) { + final int numbDocs = 10; + for (int i = 0; i < numbDocs; i++) { + db.collection(CNAME1).insertDocument(new BaseDocument(), null); + } + + final int batchSize = 5; + final ArangoCursor cursor = db.query("for i in " + CNAME1 + " return i._id", null, + new AqlQueryOptions().batchSize(batchSize).count(true), String.class); + assertThat((Object) cursor).isNotNull(); + assertThat(cursor.getCount()).isGreaterThanOrEqualTo(numbDocs); + + final ArangoCursor cursor2 = db.cursor(cursor.getId(), String.class); + assertThat((Object) cursor2).isNotNull(); + assertThat(cursor2.getCount()).isGreaterThanOrEqualTo(numbDocs); + assertThat((Iterator) cursor2).hasNext(); + + for (int i = 0; i < batchSize; i++, cursor.next()) { + assertThat((Iterator) cursor).hasNext(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void changeQueryTrackingProperties(ArangoDatabase db) { + try { + QueryTrackingPropertiesEntity properties = db.getQueryTrackingProperties(); + 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); + assertThat(properties).isNotNull(); + assertThat(properties.getEnabled()).isFalse(); + properties = db.getQueryTrackingProperties(); + assertThat(properties.getEnabled()).isFalse(); + } finally { + final QueryTrackingPropertiesEntity properties = new QueryTrackingPropertiesEntity(); + properties.setEnabled(true); + db.setQueryTrackingProperties(properties); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithBindVars(ArangoDatabase 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", bindVars, null, + String.class); + + assertThat((Object) cursor).isNotNull(); + + for (int i = 0; i < 5; i++, cursor.next()) { + assertThat((Iterator) cursor).hasNext(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithRawBindVars(ArangoDatabase 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}", bindVars, null, JsonNode.class).next(); + + assertThat(res.get("foo").textValue()).isEqualTo("fooValue"); + assertThat(res.get("bar").intValue()).isEqualTo(11); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void queryWithWarning(ArangoDB arangoDB) { + final ArangoCursor cursor = arangoDB.db().query("return 1/0", null, null, String.class); + + assertThat((Object) cursor).isNotNull(); + assertThat(cursor.getWarnings()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryStream(ArangoDatabase db) { + if (isAtLeastVersion(3, 4)) { + final ArangoCursor cursor = db + .query("FOR i IN 1..2 RETURN i", null, new AqlQueryOptions().stream(true).count(true), + Void.class); + assertThat((Object) cursor).isNotNull(); + assertThat(cursor.getCount()).isNull(); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("arangos") + void queryClose(ArangoDB arangoDB) throws IOException { + final ArangoCursor cursor = arangoDB.db() + .query("for i in 1..2 return i", null, new AqlQueryOptions().batchSize(1), String.class); + 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("dbs") + void queryNoResults(ArangoDatabase db) throws IOException { + final ArangoCursor cursor = db + .query("FOR i IN @@col RETURN i", new MapBuilder().put("@col", CNAME1).get(), null, + BaseDocument.class); + cursor.close(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryWithNullBindParam(ArangoDatabase db) throws IOException { + final ArangoCursor cursor = db.query("FOR i IN @@col FILTER i.test == @test RETURN i", + new MapBuilder().put("@col", CNAME1).put("test", null).get(), null, BaseDocument.class); + cursor.close(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void queryAllowDirtyRead(ArangoDatabase db) throws IOException { + final ArangoCursor cursor = db.query("FOR i IN @@col FILTER i.test == @test RETURN i", + new MapBuilder().put("@col", CNAME1).put("test", null).get(), + new AqlQueryOptions().allowDirtyRead(true), BaseDocument.class); + if (isAtLeastVersion(3, 10)) { + assertThat(cursor.isPotentialDirtyRead()).isTrue(); + } + cursor.close(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void explainQuery(ArangoDatabase db) { + final AqlExecutionExplainEntity explain = db.explainQuery("for i in 1..1 return i", null, null); + 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(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void explainQueryWithBindVars(ArangoDatabase db) { + final AqlExecutionExplainEntity explain = db.explainQuery("for i in 1..1 return @value", + Collections.singletonMap("value", 11), null); + 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("dbs") + void explainQueryWithIndexNode(ArangoDatabase 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("dbs") + void parseQuery(ArangoDatabase db) { + final AqlParseEntity parse = db.parseQuery("for i in 1..1 return i"); + assertThat(parse).isNotNull(); + assertThat(parse.getBindVars()).isEmpty(); + assertThat(parse.getCollections()).isEmpty(); + assertThat(parse.getAst()).hasSize(1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getCurrentlyRunningQueries(ArangoDatabase db) throws InterruptedException { + String query = "return sleep(1)"; + Thread t = new Thread(() -> db.query(query, null, null, Void.class)); + t.start(); + Thread.sleep(300); + final Collection currentlyRunningQueries = db.getCurrentlyRunningQueries(); + assertThat(currentlyRunningQueries).hasSize(1); + final QueryEntity queryEntity = currentlyRunningQueries.iterator().next(); + assertThat(queryEntity.getQuery()).isEqualTo(query); + assertThat(queryEntity.getState()).isEqualTo(QueryExecutionState.EXECUTING); + t.join(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void killQuery(ArangoDatabase db) throws InterruptedException, ExecutionException { + ExecutorService es = Executors.newSingleThreadExecutor(); + Future future = es.submit(() -> { + try { + db.query("return sleep(5)", null, null, 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("dbs") + void getAndClearSlowQueries(ArangoDatabase db) { + db.clearSlowQueries(); + + final QueryTrackingPropertiesEntity properties = db.getQueryTrackingProperties(); + final Long slowQueryThreshold = properties.getSlowQueryThreshold(); + properties.setSlowQueryThreshold(1L); + db.setQueryTrackingProperties(properties); + + db.query("return sleep(1.1)", null, null, Void.class); + final Collection slowQueries = db.getSlowQueries(); + assertThat(slowQueries).hasSize(1); + final QueryEntity queryEntity = slowQueries.iterator().next(); + assertThat(queryEntity.getQuery()).isEqualTo("return sleep(1.1)"); + + db.clearSlowQueries(); + assertThat(db.getSlowQueries()).isEmpty(); + properties.setSlowQueryThreshold(slowQueryThreshold); + db.setQueryTrackingProperties(properties); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createGetDeleteAqlFunction(ArangoDatabase db) { + final Collection aqlFunctionsInitial = db.getAqlFunctions(null); + assertThat(aqlFunctionsInitial).isEmpty(); + try { + db.createAqlFunction("myfunctions::temperature::celsiustofahrenheit", + "function (celsius) { return celsius * 1.8 + 32; }", null); + + final Collection aqlFunctions = db.getAqlFunctions(null); + assertThat(aqlFunctions).hasSizeGreaterThan(aqlFunctionsInitial.size()); + } finally { + final Integer deleteCount = db.deleteAqlFunction("myfunctions::temperature::celsiustofahrenheit", null); + // compatibility with ArangoDB < 3.4 + if (isAtLeastVersion(3, 4)) { + assertThat(deleteCount).isEqualTo(1); + } else { + assertThat(deleteCount).isNull(); + } + final Collection aqlFunctions = db.getAqlFunctions(null); + assertThat(aqlFunctions).hasSize(aqlFunctionsInitial.size()); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createGetDeleteAqlFunctionWithNamespace(ArangoDatabase db) { + final Collection aqlFunctionsInitial = db.getAqlFunctions(null); + assertThat(aqlFunctionsInitial).isEmpty(); + try { + db.createAqlFunction("myfunctions::temperature::celsiustofahrenheit1", + "function (celsius) { return celsius * 1.8 + 32; }", null); + db.createAqlFunction("myfunctions::temperature::celsiustofahrenheit2", + "function (celsius) { return celsius * 1.8 + 32; }", null); + + } finally { + final Integer deleteCount = db + .deleteAqlFunction("myfunctions::temperature", new AqlFunctionDeleteOptions().group(true)); + // compatibility with ArangoDB < 3.4 + if (isAtLeastVersion(3, 4)) { + assertThat(deleteCount).isEqualTo(2); + } else { + assertThat(deleteCount).isNull(); + } + final Collection aqlFunctions = db.getAqlFunctions(null); + assertThat(aqlFunctions).hasSize(aqlFunctionsInitial.size()); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createGraph(ArangoDatabase db) { + String name = "graph-" + rnd(); + final GraphEntity result = db.createGraph(name, null, null); + assertThat(result.getName()).isEqualTo(name); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createGraphSatellite(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 7)); + assumeTrue(isCluster()); + assumeTrue(isEnterprise()); + + String name = "graph-" + rnd(); + final GraphEntity result = db.createGraph(name, null, new GraphCreateOptions().replicationFactor(ReplicationFactor.ofSatellite())); + 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("dbs") + void createGraphReplicationFaktor(ArangoDatabase db) { + assumeTrue(isCluster()); + String name = "graph-" + rnd(); + final String edgeCollection = "edge-" + rnd(); + final String fromCollection = "from-" + rnd(); + final String toCollection = "to-" + rnd(); + final Collection edgeDefinitions = + Collections.singletonList(new EdgeDefinition().collection(edgeCollection).from(fromCollection).to(toCollection)); + final GraphEntity result = db.createGraph(name, edgeDefinitions, new GraphCreateOptions().replicationFactor(2)); + assertThat(result).isNotNull(); + for (final String collection : Arrays.asList(edgeCollection, fromCollection, toCollection)) { + final CollectionPropertiesEntity properties = db.collection(collection).getProperties(); + assertThat(properties.getReplicationFactor().getValue()).isEqualTo(2); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createGraphNumberOfShards(ArangoDatabase db) { + assumeTrue(isCluster()); + String name = "graph-" + rnd(); + final String edgeCollection = "edge-" + rnd(); + final String fromCollection = "from-" + rnd(); + final String toCollection = "to-" + rnd(); + final Collection edgeDefinitions = + Collections.singletonList(new EdgeDefinition().collection(edgeCollection).from(fromCollection).to(toCollection)); + final GraphEntity result = db + .createGraph(name, edgeDefinitions, new GraphCreateOptions().numberOfShards(2)); + 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("dbs") + void getGraphs(ArangoDatabase db) { + String name = "graph-" + rnd(); + db.createGraph(name, null, null); + final Collection graphs = db.getGraphs(); + assertThat(graphs).hasSizeGreaterThanOrEqualTo(1); + long count = graphs.stream().map(GraphEntity::getName).filter(name::equals).count(); + assertThat(count).isEqualTo(1L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionString(ArangoDatabase db) { + final TransactionOptions options = new TransactionOptions().params("test"); + final RawJson result = db.transaction("function (params) {return params;}", RawJson.class, options); + assertThat(result.getValue()).isEqualTo("\"test\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionNumber(ArangoDatabase db) { + final TransactionOptions options = new TransactionOptions().params(5); + final Integer result = db.transaction("function (params) {return params;}", Integer.class, options); + assertThat(result).isEqualTo(5); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionVPack(ArangoDatabase db) { + final TransactionOptions options = new TransactionOptions().params(JsonNodeFactory.instance.textNode("test")); + final JsonNode result = db.transaction("function (params) {return params;}", JsonNode.class, options); + assertThat(result.isTextual()).isTrue(); + assertThat(result.asText()).isEqualTo("test"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionJsonObject(ArangoDatabase db) { + 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); + assertThat(result.getValue()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionJsonArray(ArangoDatabase db) { + 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); + assertThat(result.getValue()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionMap(ArangoDatabase db) { + 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); + assertThat(result.getValue()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionArray(ArangoDatabase db) { + 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); + assertThat(result.getValue()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionCollection(ArangoDatabase db) { + 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); + assertThat(result.getValue()).isEqualTo("\"hello world\""); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionInsertJson(ArangoDatabase 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("dbs") + void transactionExclusiveWrite(ArangoDatabase 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("dbs") + void transactionEmpty(ArangoDatabase db) { + db.transaction("function () {}", Void.class, null); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionAllowImplicit(ArangoDatabase db) { + 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); + options.allowImplicit(false); + Throwable thrown = catchThrowable(() -> db.transaction(action, JsonNode.class, options)); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .extracting(it -> ((ArangoDBException) it).getResponseCode()) + .isEqualTo(400); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionPojoReturn(ArangoDatabase db) { + final String action = "function() { return {'value':'hello world'}; }"; + final TransactionTestEntity res = db.transaction(action, TransactionTestEntity.class, new TransactionOptions()); + assertThat(res).isNotNull(); + assertThat(res.value).isEqualTo("hello world"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getInfo(ArangoDatabase db) { + final DatabaseEntity info = db.getInfo(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(TEST_DB.get()); + 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("dbs") + void getDocument(ArangoDatabase db) { + String key = "key-" + rnd(); + final BaseDocument value = new BaseDocument(key); + db.collection(CNAME1).insertDocument(value); + final BaseDocument document = db.getDocument(CNAME1 + "/" + key, BaseDocument.class); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(key); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void shouldIncludeExceptionMessage(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + + final String exceptionMessage = "My error context"; + final String action = "function (params) {" + "throw '" + exceptionMessage + "';" + "}"; + try { + db.transaction(action, Void.class, null); + fail(); + } catch (final ArangoDBException e) { + assertThat(e.getErrorMessage()).isEqualTo(exceptionMessage); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getDocumentWrongId(ArangoDatabase db) { + Throwable thrown = catchThrowable(() -> db.getDocument("123", BaseDocument.class)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void reloadRouting(ArangoDatabase 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/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDocumentUtilTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDocumentUtilTest.java new file mode 100644 index 000000000..224aea3cc --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoDocumentUtilTest.java @@ -0,0 +1,84 @@ +/* + * 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.internal.util.DocumentUtil; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoDocumentUtilTest { + + @Test + void validateDocumentKeyValid() { + checkDocumentKey("1test"); + checkDocumentKey("test1"); + checkDocumentKey("test-1"); + checkDocumentKey("test_1"); + checkDocumentKey("_test"); + } + + @Test + void validateDocumentKeyInvalidSlash() { + Throwable thrown = catchThrowable(() -> checkDocumentKey("test/test")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @Test + void validateDocumentKeyEmpty() { + Throwable thrown = catchThrowable(() -> checkDocumentKey("")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + private void checkDocumentKey(final String key) throws ArangoDBException { + DocumentUtil.validateDocumentKey(key); + } + + @Test + void validateDocumentIdValid() { + checkDocumentId("1test/1test"); + checkDocumentId("test1/test1"); + checkDocumentId("test-1/test-1"); + checkDocumentId("test_1/test_1"); + checkDocumentId("_test/_test"); + } + + @Test + void validateDocumentIdInvalidWithoutSlash() { + Throwable thrown = catchThrowable(() -> checkDocumentId("test")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @Test + void validateDocumentIdEmpty() { + Throwable thrown = catchThrowable(() -> checkDocumentId("")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + private void checkDocumentId(final String id) throws ArangoDBException { + DocumentUtil.validateDocumentId(id); + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoEdgeCollectionTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoEdgeCollectionTest.java new file mode 100644 index 000000000..d25ed0e69 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoEdgeCollectionTest.java @@ -0,0 +1,445 @@ +/* + * 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.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 ArangoEdgeCollectionTest extends BaseJunit5 { + + private static final String GRAPH_NAME = "EdgeCollectionTest_graph"; + private static final String VERTEX_COLLECTION_NAME = "EdgeCollectionTest_vertex_collection"; + private static final String EDGE_COLLECTION_NAME = "EdgeCollectionTest_edge_collection"; + + private static Stream args() { + return dbsStream() + .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(ArangoVertexCollection vertices) { + final VertexEntity v1 = vertices.insertVertex(new BaseDocument()); + final VertexEntity v2 = vertices.insertVertex(new BaseDocument()); + + final BaseEdgeDocument value = new BaseEdgeDocument(); + value.setFrom(v1.getId()); + value.setTo(v2.getId()); + return value; + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void insertEdge(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value); + assertThat(edge).isNotNull(); + final BaseEdgeDocument document = edges.getEdge(edge.getKey(), BaseEdgeDocument.class); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + assertThat(document.getFrom()).isNotNull(); + assertThat(document.getTo()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void insertEdgeUpdateRev(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value); + assertThat(value.getRevision()).isNull(); + assertThat(edge.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void insertEdgeViolatingUniqueConstraint(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + edges.graph().db().collection(EDGE_COLLECTION_NAME) + .ensurePersistentIndex(Arrays.asList("_from", "_to"), new PersistentIndexOptions().unique(true)); + + BaseEdgeDocument edge = createEdgeValue(vertices); + edges.insertEdge(edge); + + try { + edges.insertEdge(edge); + } catch (ArangoDBException e) { + assertThat(e.getResponseCode()).isEqualTo(409); + assertThat(e.getErrorNum()).isEqualTo(1210); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void getEdge(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value); + final BaseEdgeDocument document = edges + .getEdge(edge.getKey(), BaseEdgeDocument.class); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + assertThat(document.getFrom()).isNotNull(); + assertThat(document.getTo()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void getEdgeIfMatch(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch(edge.getRev()); + final BaseDocument document = edges.getEdge(edge.getKey(), + BaseDocument.class, options); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void getEdgeIfMatchFail(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch("no"); + final BaseEdgeDocument edge2 = edges.getEdge(edge.getKey(), + BaseEdgeDocument.class, options); + assertThat(edge2).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void getEdgeIfNoneMatch(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch("no"); + final BaseDocument document = edges.getEdge(edge.getKey(), + BaseDocument.class, options); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void getEdgeIfNoneMatchFail(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument value = createEdgeValue(vertices); + final EdgeEntity edge = edges.insertEdge(value); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch(edge.getRev()); + final BaseEdgeDocument edge2 = edges.getEdge(edge.getKey(), + BaseEdgeDocument.class, options); + assertThat(edge2).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void replaceEdge(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final EdgeUpdateEntity replaceResult = edges + .replaceEdge(createResult.getKey(), doc); + 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); + 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("args") + void replaceEdgeUpdateRev(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc); + final EdgeUpdateEntity replaceResult = edges + .replaceEdge(createResult.getKey(), doc); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(replaceResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void replaceEdgeIfMatch(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final EdgeReplaceOptions options = new EdgeReplaceOptions().ifMatch(createResult.getRev()); + final EdgeUpdateEntity replaceResult = edges + .replaceEdge(createResult.getKey(), doc, options); + 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); + 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("args") + void replaceEdgeIfMatchFail(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final EdgeReplaceOptions options = new EdgeReplaceOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> edges.replaceEdge(createResult.getKey(), doc, options)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void updateEdge(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = edges.insertEdge(doc); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc); + 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); + 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("args") + void updateEdgeUpdateRev(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(updateResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void updateEdgeIfMatch(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = edges.insertEdge(doc); + 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); + 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); + 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("args") + void updateEdgeIfMatchFail(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = edges.insertEdge(doc); + 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)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void updateEdgeKeepNullTrue(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc); + doc.updateAttribute("a", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().keepNull(true); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc, options); + 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); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties().keySet()).hasSize(6); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void updateEdgeKeepNullFalse(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = edges.insertEdge(doc); + doc.updateAttribute("a", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().keepNull(false); + final EdgeUpdateEntity updateResult = edges + .updateEdge(createResult.getKey(), doc, options); + 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); + 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("args") + void deleteEdge(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc); + edges.deleteEdge(createResult.getKey()); + final BaseEdgeDocument edge = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class); + assertThat(edge).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void deleteEdgeIfMatch(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc); + final EdgeDeleteOptions options = new EdgeDeleteOptions().ifMatch(createResult.getRev()); + edges.deleteEdge(createResult.getKey(), options); + final BaseEdgeDocument edge = edges + .getEdge(createResult.getKey(), BaseEdgeDocument.class); + assertThat(edge).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void deleteEdgeIfMatchFail(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument doc = createEdgeValue(vertices); + final EdgeEntity createResult = edges.insertEdge(doc); + final EdgeDeleteOptions options = new EdgeDeleteOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> edges.deleteEdge(createResult.getKey(), options)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("args") + void edgeKeyWithSpecialChars(ArangoVertexCollection vertices, ArangoEdgeCollection edges) { + final BaseEdgeDocument value = createEdgeValue(vertices); + final String key = "_-:.@()+,=;$!*'%" + UUID.randomUUID(); + value.setKey(key); + final EdgeEntity edge = edges.insertEdge(value); + assertThat(edge).isNotNull(); + final BaseEdgeDocument document = edges.getEdge(edge.getKey(), BaseEdgeDocument.class); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(key); + assertThat(document.getFrom()).isNotNull(); + assertThat(document.getTo()).isNotNull(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoGraphTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoGraphTest.java new file mode 100644 index 000000000..73e56711c --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoGraphTest.java @@ -0,0 +1,438 @@ +/* + * 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.GraphCreateOptions; +import com.arangodb.model.VertexCollectionCreateOptions; +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.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 ArangoGraphTest extends BaseJunit5 { + + private static final String GRAPH_NAME = "ArangoGraphTest_graph"; + + private static final String VERTEX_COL_1 = "ArangoGraphTest_vertex_collection_1"; + private static final String VERTEX_COL_2 = "ArangoGraphTest_vertex_collection_2"; + private static final String VERTEX_COL_3 = "ArangoGraphTest_vertex_collection_3"; + private static final String VERTEX_COL_4 = "ArangoGraphTest_vertex_collection_4"; + + private static final String EDGE_COL_1 = "ArangoGraphTest_edge_collection_1"; + private static final String EDGE_COL_2 = "ArangoGraphTest_edge_collection_2"; + private static final String EDGE_COL_3 = "ArangoGraphTest_edge_collection_3"; + + 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_2); + 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 graphs() { + return dbsStream() + .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("graphs") + void exists(ArangoGraph graph) { + assertThat(graph.exists()).isTrue(); + assertThat(graph.db().graph(GRAPH_NAME + "no").exists()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createWithReplicationAndWriteConcern(ArangoDatabase db) { + 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)); + assertThat(graph).isNotNull(); + assertThat(graph.getName()).isEqualTo(GRAPH_NAME + "_1"); + assertThat(graph.getWriteConcern()).isEqualTo(2); + assertThat(graph.getReplicationFactor().getValue()).isEqualTo(2); + db.graph(GRAPH_NAME + "_1").drop(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getGraphs(ArangoDatabase db) { + final Collection graphs = db.getGraphs(); + assertThat(graphs.stream().anyMatch(it -> it.getName().equals(GRAPH_NAME))).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("graphs") + void getInfo(ArangoGraph graph) { + final GraphEntity info = graph.getInfo(); + assertThat(info).isNotNull(); + assertThat(info.getName()).isEqualTo(GRAPH_NAME); + assertThat(info.getEdgeDefinitions()).hasSize(2); + final Iterator iterator = info.getEdgeDefinitions().iterator(); + final EdgeDefinition e1 = iterator.next(); + assertThat(e1.getCollection()).isEqualTo(EDGE_COL_1); + assertThat(e1.getFrom()).contains(VERTEX_COL_1); + assertThat(e1.getTo()).contains(VERTEX_COL_2); + final EdgeDefinition e2 = iterator.next(); + 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}) { + final CollectionPropertiesEntity properties = graph.db().collection(collection).getProperties(); + assertThat(properties.getReplicationFactor().getValue()).isEqualTo(REPLICATION_FACTOR); + assertThat(properties.getNumberOfShards()).isEqualTo(NUMBER_OF_SHARDS); + } + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("graphs") + void getVertexCollections(ArangoGraph graph) { + final Collection vertexCollections = graph.getVertexCollections(); + assertThat(vertexCollections) + .hasSize(3) + .contains(VERTEX_COL_1, VERTEX_COL_2, VERTEX_COL_3); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("graphs") + void addVertexCollection(ArangoGraph graph) { + final GraphEntity g = graph.addVertexCollection(VERTEX_COL_4); + assertThat(g).isNotNull(); + final Collection vertexCollections = graph.getVertexCollections(); + assertThat(vertexCollections).contains(VERTEX_COL_1, VERTEX_COL_2, VERTEX_COL_3, VERTEX_COL_4); + + // revert + graph.vertexCollection(VERTEX_COL_4).drop(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void addSatelliteVertexCollection(ArangoDatabase db) { + assumeTrue(isCluster() || isAtLeastVersion(3, 10)); + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 9)); + + String v1Name = "vertex-" + rnd(); + + ArangoGraph 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(); + assertThat(vertexCollections).contains(v1Name); + assertThat(db.collection(v1Name).getProperties().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + + // revert + g.drop(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("graphs") + void getEdgeCollections(ArangoGraph graph) { + final Collection edgeCollections = graph.getEdgeDefinitions(); + assertThat(edgeCollections) + .hasSize(2) + .contains(EDGE_COL_1, EDGE_COL_2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("graphs") + void addEdgeDefinition(ArangoGraph graph) { + EdgeDefinition ed = new EdgeDefinition().collection(EDGE_COL_3).from(VERTEX_COL_1).to(VERTEX_COL_2); + final GraphEntity g = graph.addEdgeDefinition(ed); + 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(); + assertThat(properties.getReplicationFactor().getValue()).isEqualTo(REPLICATION_FACTOR); + assertThat(properties.getNumberOfShards()).isEqualTo(NUMBER_OF_SHARDS); + } + + // revert + graph.removeEdgeDefinition(EDGE_COL_3); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void addSatelliteEdgeDefinition(ArangoDatabase db) { + 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); + + ArangoGraph g = db.graph(GRAPH_NAME + rnd()); + g.create(null, new GraphCreateOptions().isSmart(true).smartGraphAttribute("test")); + g.addEdgeDefinition(ed); + final GraphEntity ge = g.getInfo(); + 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().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + + // revert + g.drop(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("graphs") + void replaceEdgeDefinition(ArangoGraph graph) { + final GraphEntity g = graph + .replaceEdgeDefinition(new EdgeDefinition().collection(EDGE_COL_1).from(VERTEX_COL_3).to(VERTEX_COL_4)); + 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); + } + } + + // revert + graph.removeEdgeDefinition(EDGE_COL_1); + graph.vertexCollection(VERTEX_COL_4).drop(); + graph.addEdgeDefinition(ed1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("graphs") + void removeEdgeDefinition(ArangoGraph graph) { + final GraphEntity g = graph.removeEdgeDefinition(EDGE_COL_1); + final Collection edgeDefinitions = g.getEdgeDefinitions(); + assertThat(edgeDefinitions).hasSize(1); + assertThat(edgeDefinitions.iterator().next().getCollection()).isEqualTo(EDGE_COL_2); + + //revert + graph.addEdgeDefinition(ed1); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void smartGraph(ArangoDatabase db) { + 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)); + + assertThat(g).isNotNull(); + assertThat(g.getIsSmart()).isTrue(); + assertThat(g.getSmartGraphAttribute()).isEqualTo("test"); + assertThat(g.getNumberOfShards()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void hybridSmartGraph(ArangoDatabase db) { + 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)); + + assertThat(g).isNotNull(); + assertThat(g.getIsSmart()).isTrue(); + assertThat(g.getSmartGraphAttribute()).isEqualTo("test"); + assertThat(g.getNumberOfShards()).isEqualTo(2); + + assertThat(db.collection(eName).getProperties().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + assertThat(db.collection(v1Name).getProperties().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + assertThat(db.collection(v2Name).getProperties().getReplicationFactor().getValue()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void disjointSmartGraph(ArangoDatabase db) { + 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)); + + 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("dbs") + void hybridDisjointSmartGraph(ArangoDatabase db) { + 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)); + + 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().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + assertThat(db.collection(v2Name).getProperties().getReplicationFactor().getValue()).isEqualTo(2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void enterpriseGraph(ArangoDatabase db) { + 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)); + + 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("dbs") + void drop(ArangoDatabase db) { + 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))); + assertThat(result).isNotNull(); + db.graph(graphId).drop(); + assertThat(db.collection(edgeCollection).exists()).isTrue(); + assertThat(db.collection(vertexCollection).exists()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void dropPlusDropCollections(ArangoDatabase db) { + 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))); + assertThat(result).isNotNull(); + db.graph(graphId).drop(true); + assertThat(db.collection(edgeCollection).exists()).isFalse(); + assertThat(db.collection(vertexCollection).exists()).isFalse(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoSearchTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoSearchTest.java new file mode 100644 index 000000000..deaecc588 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoSearchTest.java @@ -0,0 +1,1010 @@ +/* + * 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.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.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; + +import java.util.*; + +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 ArangoSearchTest 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("dbs") + void exists(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + db.createArangoSearch(viewName, new ArangoSearchCreateOptions()); + assertThat(db.arangoSearch(viewName).exists()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createAndExistsSearchAlias(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = "view-" + rnd(); + db.createSearchAlias(viewName, new SearchAliasCreateOptions()); + assertThat(db.arangoSearch(viewName).exists()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getInfo(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + db.createArangoSearch(viewName, new ArangoSearchCreateOptions()); + final ViewEntity info = db.arangoSearch(viewName).getInfo(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void drop(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + db.createArangoSearch(viewName, new ArangoSearchCreateOptions()); + final ArangoView view = db.arangoSearch(viewName); + view.drop(); + assertThat(view.exists()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void rename(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + final String name = viewName + "_new"; + db.createArangoSearch(name, new ArangoSearchCreateOptions()); + db.arangoSearch(name).rename(viewName); + assertThat(db.arangoSearch(name).exists()).isFalse(); + assertThat(db.arangoSearch(viewName).exists()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createArangoSearchView(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + final ViewEntity info = db.arangoSearch(viewName).create(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(viewName).exists()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createSearchAliasView(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = "view-" + rnd(); + 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("dbs") + void createArangoSearchViewWithOptions(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + final ArangoSearchCreateOptions options = new ArangoSearchCreateOptions(); + final ViewEntity info = db.arangoSearch(viewName).create(options); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(viewName).exists()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createArangoSearchViewWithPrimarySort(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 5)); + String viewName = "view-" + rnd(); + 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 ArangoSearch view = db.arangoSearch(viewName); + final ViewEntity info = view.create(options); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(viewName).exists()).isTrue(); + + if (isAtLeastVersion(3, 7)) { + final ArangoSearchPropertiesEntity properties = view.getProperties(); + 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("dbs") + void createArangoSearchViewWithCommitIntervalMsec(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 5)); + String viewName = "view-" + rnd(); + final ArangoSearchCreateOptions options = new ArangoSearchCreateOptions(); + options.commitIntervalMsec(666666L); + + final ViewEntity info = db.arangoSearch(viewName).create(options); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(viewName); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(viewName).exists()).isTrue(); + + // check commit interval msec property + final ArangoSearch view = db.arangoSearch(viewName); + final ArangoSearchPropertiesEntity properties = view.getProperties(); + assertThat(properties.getCommitIntervalMsec()).isEqualTo(666666L); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createSearchAliasViewWithOptions(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = "view-" + rnd(); + 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("dbs") + void createSearchAliasViewWithIndexesAndGetProperties(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollection col = db.collection(COLL_1); + String idxName1 = "idx-" + rnd(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName1) + .fields(new InvertedIndexField().name("a" + rnd()))); + + String idxName2 = "idx-" + rnd(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName2) + .fields(new InvertedIndexField().name("a" + rnd()))); + + String viewName = "view-" + rnd(); + 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("dbs") + void getArangoSearchViewProperties(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + final ArangoSearch view = db.arangoSearch(viewName); + view.create(new ArangoSearchCreateOptions()); + final ArangoSearchPropertiesEntity properties = view.getProperties(); + 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("dbs") + void updateArangoSearchViewProperties(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + final ArangoSearch view = db.arangoSearch(viewName); + view.create(new ArangoSearchCreateOptions()); + 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); + 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("dbs") + void updateSearchAliasViewWithIndexesAndGetProperties(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollection col = db.collection(COLL_1); + String idxName = "idx-" + rnd(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName) + .fields(new InvertedIndexField().name("a" + rnd()))); + ArangoCollection col2 = db.collection(COLL_2); + String idxName2 = "idx-" + rnd(); + col2.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName2) + .fields(new InvertedIndexField().name("a" + rnd()))); + + String viewName = "view-" + rnd(); + 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("dbs") + void replaceArangoSearchViewProperties(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + final ArangoSearch view = db.arangoSearch(viewName); + view.create(new ArangoSearchCreateOptions()); + final ArangoSearchPropertiesOptions options = new ArangoSearchPropertiesOptions(); + options.link(CollectionLink.on(COLL_1) + .fields(FieldLink.on("value").analyzers("identity"))); + final ArangoSearchPropertiesEntity properties = view.replaceProperties(options); + 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("dbs") + void replaceSearchAliasViewWithIndexesAndGetProperties(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollection col = db.collection(COLL_1); + String idxName = "idx-" + rnd(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName) + .fields(new InvertedIndexField().name("a" + rnd()))); + ArangoCollection col2 = db.collection(COLL_2); + String idxName2 = "idx-" + rnd(); + col2.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName2) + .fields(new InvertedIndexField().name("a" + rnd()))); + + String viewName = "view-" + rnd(); + 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(ArangoDatabase db, SearchAnalyzer analyzer) { + + String fullyQualifiedName = db.dbName().get() + "::" + analyzer.getName(); + analyzer.setName(fullyQualifiedName); + + // createAnalyzer + SearchAnalyzer createdAnalyzer = db.createSearchAnalyzer(analyzer); + assertThat(createdAnalyzer).isEqualTo(analyzer); + + // getAnalyzer + SearchAnalyzer gotAnalyzer = db.getSearchAnalyzer(analyzer.getName()); + assertThat(gotAnalyzer).isEqualTo(analyzer); + + // getAnalyzers + SearchAnalyzer foundAnalyzer = + db.getSearchAnalyzers().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); + + Throwable thrown = catchThrowable(() -> db.getSearchAnalyzer(analyzer.getName())); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(404); + assertThat(e.getErrorNum()).isEqualTo(1202); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void identityAnalyzerTyped(ArangoDatabase db) { + 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("dbs") + void delimiterAnalyzerTyped(ArangoDatabase db) { + 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("dbs") + void stemAnalyzerTyped(ArangoDatabase db) { + 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("dbs") + void normAnalyzerTyped(ArangoDatabase db) { + 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("dbs") + void ngramAnalyzerTyped(ArangoDatabase db) { + 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("dbs") + void enhancedNgramAnalyzerTyped(ArangoDatabase db) { + 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("dbs") + void textAnalyzerTyped(ArangoDatabase db) { + 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("dbs") + void enhancedTextAnalyzerTyped(ArangoDatabase db) { + 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("dbs") + void arangoSearchOptions(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 4)); + String viewName = "view-" + rnd(); + FieldLink field = FieldLink.on("f1").inBackground(true); + 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); + StoredValue storedValue = new StoredValue(Arrays.asList("a", "b"), ArangoSearchCompression.none, true); + options.storedValues(storedValue); + + final ArangoSearch view = db.arangoSearch(viewName); + view.create(options); + + final ArangoSearchPropertiesEntity properties = view.getProperties(); + 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(); + + if (isEnterprise() && isAtLeastVersion(3, 9, 5) && isLessThanVersion(3, 10)) { + assertThat(createdLink.getCache()).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"); + } + + FieldLink fieldLink = createdLink.getFields().iterator().next(); + 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"); + } + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void pipelineAnalyzer(ArangoDatabase db) { + 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("dbs") + void stopwordsAnalyzer(ArangoDatabase db) { + 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))", + Collections.singletonMap("aName", name), Collection.class).next(); + assertThat(res).containsExactly("fox", "dog", "a", "theater"); + db.deleteSearchAnalyzer(name); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void aqlAnalyzer(ArangoDatabase db) { + 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("dbs") + void geoJsonAnalyzer(ArangoDatabase db) { + 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("dbs") + void geoPointAnalyzer(ArangoDatabase db) { + 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("dbs") + void segmentationAnalyzer(ArangoDatabase db) { + 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("dbs") + void collationAnalyzer(ArangoDatabase db) { + 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("dbs") + void classificationAnalyzer(ArangoDatabase db) { + 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("dbs") + void nearestNeighborsAnalyzer(ArangoDatabase db) { + 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("dbs") + void MinHashAnalyzer(ArangoDatabase db) { + 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("dbs") + void offsetFeature(ArangoDatabase db) { + 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); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoSslTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoSslTest.java new file mode 100644 index 000000000..1c9c2bd3a --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoSslTest.java @@ -0,0 +1,101 @@ +/* + * 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.ArangoDBVersion; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.TrustManagerFactory; +import java.security.KeyStore; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +@Tag("ssl") +@EnabledIfSystemProperty(named = "SslTest", matches = "true") +class ArangoSslTest { + + /* + * a SSL trust store + * + * create the trust store for the self signed certificate: + * keytool -import -alias "my arangodb server cert" -file UnitTests/server.pem -keystore example.truststore + * + * Documentation: + * https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLSocketFactory.html + */ + private static final String SSL_TRUSTSTORE = "/example.truststore"; + private static final String SSL_TRUSTSTORE_PASSWORD = "12345678"; + + @ParameterizedTest + @EnumSource(Protocol.class) + void connect(Protocol protocol) throws Exception { + final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(this.getClass().getResourceAsStream(SSL_TRUSTSTORE), SSL_TRUSTSTORE_PASSWORD.toCharArray()); + + final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, SSL_TRUSTSTORE_PASSWORD.toCharArray()); + + final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + + final SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + final ArangoDB arangoDB = new ArangoDB.Builder() + .useProtocol(protocol) + .host("localhost", 8529) + .password("test") + .useSsl(true) + .sslContext(sc).build(); + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version).isNotNull(); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void connectWithoutValidSslContext(Protocol protocol) { + final ArangoDB arangoDB = new ArangoDB.Builder() + .useProtocol(protocol) + .host("localhost", 8529) + .useSsl(true) + .build(); + Throwable thrown = catchThrowable(arangoDB::getVersion); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException ex = (ArangoDBException) thrown; + assertThat(ex.getCause()).isInstanceOf(ArangoDBMultipleException.class); + List exceptions = ((ArangoDBMultipleException) ex.getCause()).getExceptions(); + exceptions.forEach(e -> assertThat(e).isInstanceOf(SSLHandshakeException.class)); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoVertexCollectionTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoVertexCollectionTest.java new file mode 100644 index 000000000..5b5504d80 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoVertexCollectionTest.java @@ -0,0 +1,480 @@ +/* + * 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.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 ArangoVertexCollectionTest extends BaseJunit5 { + + private static final String GRAPH_NAME = "ArangoVertexCollectionTest_graph"; + private static final String COLLECTION_NAME = "ArangoVertexCollectionTest_vertex_collection"; + + private static Stream vertices() { + return dbsStream() + .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("vertices") + void dropVertexCollection(ArangoVertexCollection vertices) { + ArangoGraph graph = vertices.graph(); + vertices.drop(); + final Collection vertexCollections = graph.getVertexCollections(); + assertThat(vertexCollections).isEmpty(); + + // revert + graph.addVertexCollection(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void insertVertex(ArangoVertexCollection vertices) { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null); + assertThat(vertex).isNotNull(); + ArangoCollection collection = vertices.graph().db().collection(vertices.name()); + final BaseDocument document = collection + .getDocument(vertex.getKey(), BaseDocument.class, null); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void insertVertexViolatingUniqueConstraint(ArangoVertexCollection vertices) { + ArangoCollection collection = vertices.graph().db().collection(vertices.name()); + collection + .ensureSkiplistIndex(Collections.singletonList("field"), + new SkiplistIndexOptions().unique(true).sparse(true)); + + VertexEntity inserted = vertices.insertVertex(RawJson.of("{\"field\": 99}")); + + 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("vertices") + void duplicateInsertSameObjectVertex(ArangoVertexCollection 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("vertices") + void insertVertexUpdateRev(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity vertex = vertices.insertVertex(doc, null); + assertThat(doc.getRevision()).isNull(); + assertThat(vertex.getRev()).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void getVertex(ArangoVertexCollection vertices) { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null); + final BaseDocument document = vertices + .getVertex(vertex.getKey(), BaseDocument.class, null); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void getVertexIfMatch(ArangoVertexCollection vertices) { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch(vertex.getRev()); + final BaseDocument document = vertices + .getVertex(vertex.getKey(), BaseDocument.class, options); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void getVertexIfMatchFail(ArangoVertexCollection vertices) { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch("no"); + final BaseDocument vertex2 = vertices + .getVertex(vertex.getKey(), BaseDocument.class, options); + assertThat(vertex2).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void getVertexIfNoneMatch(ArangoVertexCollection vertices) { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch("no"); + final BaseDocument document = vertices + .getVertex(vertex.getKey(), BaseDocument.class, options); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void getVertexIfNoneMatchFail(ArangoVertexCollection vertices) { + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(), null); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch(vertex.getRev()); + final BaseDocument vertex2 = vertices + .getVertex(vertex.getKey(), BaseDocument.class, options); + assertThat(vertex2).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void replaceVertex(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final VertexUpdateEntity replaceResult = vertices + .replaceVertex(createResult.getKey(), doc, null); + 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); + 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("vertices") + void replaceVertexUpdateRev(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + final VertexUpdateEntity replaceResult = vertices + .replaceVertex(createResult.getKey(), doc, null); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(replaceResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void replaceVertexIfMatch(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final VertexReplaceOptions options = new VertexReplaceOptions().ifMatch(createResult.getRev()); + final VertexUpdateEntity replaceResult = vertices + .replaceVertex(createResult.getKey(), doc, options); + 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); + 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("vertices") + void replaceVertexIfMatchFail(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final VertexReplaceOptions options = new VertexReplaceOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> vertices.replaceVertex(createResult.getKey(), doc, options)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void updateVertex(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, null); + 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); + 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("vertices") + void updateVertexUpdateRev(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + doc.addAttribute("foo", "bar"); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, null); + assertThat(doc.getRevision()).isNull(); + assertThat(createResult.getRev()).isNotNull(); + assertThat(updateResult.getRev()) + .isNotNull() + .isNotEqualTo(createResult.getRev()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void updateVertexIfMatch(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + 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); + 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); + 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("vertices") + void updateVertexIfMatchFail(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + 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)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void updateVertexKeepNullTrue(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + doc.updateAttribute("a", null); + final VertexUpdateOptions options = new VertexUpdateOptions().keepNull(true); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, options); + 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); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties().keySet()).hasSize(4); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void updateVertexKeepNullFalse(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + doc.updateAttribute("a", null); + final VertexUpdateOptions options = new VertexUpdateOptions().keepNull(false); + final VertexUpdateEntity updateResult = vertices + .updateVertex(createResult.getKey(), doc, options); + 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); + 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("vertices") + void deleteVertex(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + vertices.deleteVertex(createResult.getKey(), null); + final BaseDocument vertex = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null); + assertThat(vertex).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void deleteVertexIfMatch(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + final VertexDeleteOptions options = new VertexDeleteOptions().ifMatch(createResult.getRev()); + vertices.deleteVertex(createResult.getKey(), options); + final BaseDocument vertex = vertices + .getVertex(createResult.getKey(), BaseDocument.class, null); + assertThat(vertex).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void deleteVertexIfMatchFail(ArangoVertexCollection vertices) { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = vertices + .insertVertex(doc, null); + final VertexDeleteOptions options = new VertexDeleteOptions().ifMatch("no"); + Throwable thrown = catchThrowable(() -> vertices.deleteVertex(createResult.getKey(), options)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(412); + assertThat(e.getErrorNum()).isEqualTo(1200); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void vertexKeyWithSpecialChars(ArangoVertexCollection vertices) { + final String key = "_-:.@()+,=;$!*'%" + UUID.randomUUID(); + final VertexEntity vertex = vertices + .insertVertex(new BaseDocument(key), null); + assertThat(vertex).isNotNull(); + ArangoCollection collection = vertices.graph().db().collection(vertices.name()); + final BaseDocument document = collection + .getDocument(vertex.getKey(), BaseDocument.class, null); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(key); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoViewTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoViewTest.java new file mode 100644 index 000000000..09121b7ff --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ArangoViewTest.java @@ -0,0 +1,114 @@ +/* + * 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 static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoViewTest extends BaseJunit5 { + + @BeforeAll + static void init() { + initDB(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void exists(ArangoDatabase db) { + String name = "view-" + rnd(); + db.createView(name, ViewType.ARANGO_SEARCH); + assertThat(db.view(name).exists()).isTrue(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getInfo(ArangoDatabase db) { + String name = "view-" + rnd(); + db.createView(name, ViewType.ARANGO_SEARCH); + final ViewEntity info = db.view(name).getInfo(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(name); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getInfoSearchAlias(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + String name = "view-" + rnd(); + db.createView(name, ViewType.SEARCH_ALIAS); + final ViewEntity info = db.view(name).getInfo(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(name); + assertThat(info.getType()).isEqualTo(ViewType.SEARCH_ALIAS); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getViews(ArangoDatabase db) { + assumeTrue(isAtLeastVersion(3, 10)); + String name1 = "view-" + rnd(); + String name2 = "view-" + rnd(); + db.createView(name1, ViewType.ARANGO_SEARCH); + db.createView(name2, ViewType.SEARCH_ALIAS); + Collection views = db.getViews(); + assertThat(views).extracting(ViewEntity::getName).contains(name1, name2); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void drop(ArangoDatabase db) { + String name = "view-" + rnd(); + db.createView(name, ViewType.ARANGO_SEARCH); + final ArangoView view = db.view(name); + view.drop(); + assertThat(view.exists()).isFalse(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void rename(ArangoDatabase db) { + assumeTrue(isSingleServer()); + String oldName = "view-" + rnd(); + String newName = "view-" + rnd(); + + db.createView(oldName, ViewType.ARANGO_SEARCH); + db.view(oldName).rename(newName); + assertThat(db.view(oldName).exists()).isFalse(); + assertThat(db.view(newName).exists()).isTrue(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseJunit5.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseJunit5.java new file mode 100644 index 000000000..fd2aa8415 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/BaseJunit5.java @@ -0,0 +1,128 @@ +package com.arangodb; + +import com.arangodb.entity.*; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import com.arangodb.model.CollectionCreateOptions; +import com.arangodb.model.GraphCreateOptions; +import com.arangodb.util.TestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +class BaseJunit5 { + protected static final DbName TEST_DB = DbName.of("java_driver_shaded_test_db"); + + private static final List adbs = Arrays.asList( + new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.VST).build(), + new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP_VPACK).build(), + new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP_JSON).build(), + new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP2_VPACK).build(), + new ArangoDB.Builder().loadProperties(new FileConfigPropertiesProvider()).useProtocol(Protocol.HTTP2_JSON).build() + ); + + protected static Stream dbsStream() { + return adbs.stream().map(adb -> adb.db(TEST_DB)); + } + + protected static Stream arangos() { + return adbs.stream().map(Arguments::of); + } + + protected static Stream dbs() { + return dbsStream().map(Arguments::of); + } + + static ArangoDatabase initDB(DbName name) { + ArangoDatabase database = adbs.get(0).db(name); + if (!database.exists()) + database.create(); + return database; + } + + static ArangoDatabase initDB() { + return initDB(TEST_DB); + } + + static void dropDB(DbName name) { + ArangoDatabase database = adbs.get(0).db(name); + if (database.exists()) + database.drop(); + } + + static void initGraph(String name, Collection edgeDefinitions, GraphCreateOptions options) { + ArangoDatabase db = initDB(); + db.createGraph(name, edgeDefinitions, options); + } + + static void initCollections(String... collections) { + ArangoDatabase db = initDB(); + for (String collection : collections) { + if (db.collection(collection).exists()) + db.collection(collection).drop(); + db.createCollection(collection, null); + } + } + + static void initEdgeCollections(String... collections) { + ArangoDatabase db = initDB(); + for (String collection : collections) { + if (db.collection(collection).exists()) + db.collection(collection).drop(); + db.createCollection(collection, new CollectionCreateOptions().type(CollectionType.EDGES)); + } + } + + @BeforeAll + static void init() { + dropDB(TEST_DB); + } + + @AfterAll + static void shutdown() { + dropDB(TEST_DB); + } + + static String rnd() { + return UUID.randomUUID().toString(); + } + + boolean isAtLeastVersion(final int major, final int minor) { + return isAtLeastVersion(major, minor, 0); + } + + boolean isAtLeastVersion(final int major, final int minor, final int patch) { + return TestUtils.isAtLeastVersion(adbs.get(0).getVersion().getVersion(), major, minor, patch); + } + + boolean isLessThanVersion(final int major, final int minor) { + return isLessThanVersion(major, minor, 0); + } + + boolean isLessThanVersion(final int major, final int minor, final int patch) { + return TestUtils.isLessThanVersion(adbs.get(0).getVersion().getVersion(), major, minor, patch); + } + + boolean isStorageEngine(ArangoDBEngine.StorageEngineName name) { + return name.equals(adbs.get(0).getEngine().getName()); + } + + boolean isSingleServer() { + return adbs.get(0).getRole() == ServerRole.SINGLE; + } + + boolean isCluster() { + return adbs.get(0).getRole() == ServerRole.COORDINATOR; + } + + boolean isEnterprise() { + return adbs.get(0).getVersion().getLicense() == License.ENTERPRISE; + } + + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/ConcurrencyTests.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ConcurrencyTests.java new file mode 100644 index 000000000..1103cb6df --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ConcurrencyTests.java @@ -0,0 +1,34 @@ +package com.arangodb; + +import com.arangodb.internal.config.FileConfigPropertiesProvider; +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; + +class ConcurrencyTests { + + @ParameterizedTest + @EnumSource(Protocol.class) + void concurrentPendingRequests(Protocol protocol) throws ExecutionException, InterruptedException { + ExecutorService es = Executors.newFixedThreadPool(10); + ArangoDB adb = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .useProtocol(protocol).build(); + 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/shaded-integration-tests/src/test/jackson/java/com/arangodb/DocumentTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/DocumentTest.java new file mode 100644 index 000000000..0ce14ff53 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/DocumentTest.java @@ -0,0 +1,138 @@ +/* + * 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.DocumentCreateEntity; +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.Map; +import java.util.UUID; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class DocumentTest extends BaseJunit5 { + + private static final String COLLECTION_NAME = "DocumentTest_collection"; + + private static Stream cols() { + return dbsStream() + .map(db -> db.collection(COLLECTION_NAME)) + .map(Arguments::of); + } + + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertAsJson(ArangoCollection collection) { + //@formatter:off + final RawJson json = RawJson.of( + "{" + + "\"article\": {" + + "\"artist\": \"PREGARDIEN/RHEINISCHE KANTOREI/DAS\"," + + "\"releaseDate\": \"1970-01-01\"," + + "\"composer\": \"BACH\"," + + "\"format\": \"CD\"," + + "\"vat\": \"H\"," + + "\"carriers\": 1," + + "\"label\": \"CAPRICCIO\"," + + "\"title\": \"BACH ST MATTHEW PASSION BWV244\"," + + "\"barcode\": [" + + "\"4006408600466\"" + + "]," + + "\"conductor\": \"MAX, H.\"" + + "}," + + "\"stock\": {" + + "\"status\": \"RMV\"," + + "\"lastUpdate\": \"2016-11-01 00:00\"" + + "}" + + "}" + ); + //@formatter:on + final DocumentCreateEntity createResult = collection.insertDocument(json); + final BaseDocument doc = collection.getDocument(createResult.getKey(), BaseDocument.class); + assertThat(doc).isNotNull(); + final Object article = doc.getAttribute("article"); + assertThat(article).isNotNull(); + final Object artist = ((Map) article).get("artist"); + assertThat(artist).isNotNull(); + assertThat(artist.toString()).isEqualTo("PREGARDIEN/RHEINISCHE KANTOREI/DAS"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void insertAsBaseDocument(ArangoCollection collection) { + final BaseDocument document = new BaseDocument(UUID.randomUUID().toString()); + { + final BaseDocument article = new BaseDocument(UUID.randomUUID().toString()); + document.addAttribute("article", article); + article.addAttribute("artist", "PREGARDIEN/RHEINISCHE KANTOREI/DAS"); + article.addAttribute("releaseDate", "1970-01-01"); + article.addAttribute("composer", "BACH"); + article.addAttribute("format", "CD"); + article.addAttribute("vat", "H"); + article.addAttribute("carriers", 1); + article.addAttribute("label", "CAPRICCIO"); + article.addAttribute("title", "BACH ST MATTHEW PASSION BWV244"); + article.addAttribute("barcode", new String[]{"4006408600466"}); + article.addAttribute("conductor", "MAX, H."); + final BaseDocument stock = new BaseDocument(UUID.randomUUID().toString()); + document.addAttribute("stock", stock); + stock.addAttribute("status", "RMV"); + stock.addAttribute("lastUpdate", "2016-11-01 00:00"); + } + final DocumentCreateEntity createResult = collection.insertDocument(document); + final BaseDocument doc = collection.getDocument(createResult.getKey(), BaseDocument.class); + assertThat(doc).isNotNull(); + final Object article = doc.getAttribute("article"); + assertThat(article).isNotNull(); + final Object artist = ((Map) article).get("artist"); + assertThat(artist).isNotNull(); + assertThat(artist.toString()).isEqualTo("PREGARDIEN/RHEINISCHE KANTOREI/DAS"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void documentKeyWithSpecialChars(ArangoCollection collection) { + final String key = "_-:.@()+,=;$!*'%" + UUID.randomUUID(); + final BaseDocument document = new BaseDocument(key); + final DocumentCreateEntity createResult = collection.insertDocument(document); + final BaseDocument doc = collection.getDocument(createResult.getKey(), BaseDocument.class); + assertThat(doc).isNotNull(); + assertThat(doc.getKey()).isEqualTo(key); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/InvertedIndexTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/InvertedIndexTest.java new file mode 100644 index 000000000..e96bc70ef --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/InvertedIndexTest.java @@ -0,0 +1,194 @@ +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.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class InvertedIndexTest extends BaseJunit5 { + + private static final String COLLECTION_NAME = "InvertedIndexTest_collection"; + + private static Stream cols() { + return dbsStream().map(db -> db.collection(COLLECTION_NAME)).map(Arguments::of); + } + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + } + + private void createAnalyzer(String analyzerName, ArangoDatabase db) { + 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); + } + + private InvertedIndexOptions createOptions(String analyzerName) { + InvertedIndexField field = new InvertedIndexField() + .name("foo") + .analyzer(AnalyzerType.identity.toString()) + .includeAllFields(true) + .searchField(false) + .trackListPositions(false) + .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("invertedIndex-" + UUID.randomUUID()) + .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) + ) + .storedValues(new StoredValue(Arrays.asList("f3", "f4"), ArangoSearchCompression.none)) + .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); + } + + 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()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void createAndGetInvertedIndex(ArangoCollection collection) { + assumeTrue(isAtLeastVersion(3, 10)); + String analyzerName = "delimiter-" + UUID.randomUUID(); + createAnalyzer(analyzerName, collection.db()); + InvertedIndexOptions options = createOptions(analyzerName); + InvertedIndexEntity created = collection.ensureInvertedIndex(options); + assertCorrectIndexEntity(created, options); + InvertedIndexEntity loadedIndex = collection.getInvertedIndex(created.getName()); + assertCorrectIndexEntity(loadedIndex, options); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getInvertedIndexesShouldNotReturnOtherIndexTypes(ArangoCollection collection) { + 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); + + Collection loadedIndexes = collection.getInvertedIndexes(); + assertThat(loadedIndexes).map(InvertedIndexEntity::getName) + .doesNotContain("persistentIndex") + .contains(created.getName()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("cols") + void getIndexesShouldNotReturnInvertedIndexes(ArangoCollection collection) { + 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); + + Collection loadedIndexes = collection.getIndexes(); + assertThat(loadedIndexes).map(IndexEntity::getName) + .doesNotContain(created.getName()) + .contains("persistentIndex"); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/JwtAuthTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/JwtAuthTest.java new file mode 100644 index 000000000..34d41a751 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/JwtAuthTest.java @@ -0,0 +1,97 @@ +package com.arangodb; + +import com.arangodb.internal.config.FileConfigPropertiesProvider; +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 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 JwtAuthTest { + + private volatile static String jwt; + + @BeforeAll + static void init() { + ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .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(DbName.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) { + ArangoDB arangoDB = getBuilder(protocol).build(); + Throwable thrown = catchThrowable(arangoDB::getVersion); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(401); + arangoDB.shutdown(); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void authenticated(Protocol protocol) { + ArangoDB arangoDB = getBuilder(protocol) + .jwt(jwt) + .build(); + arangoDB.getVersion(); + arangoDB.shutdown(); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void updateJwt(Protocol protocol) { + assumeTrue(protocol != Protocol.VST, "DE-423"); + ArangoDB arangoDB = getBuilder(protocol) + .jwt(jwt) + .build(); + arangoDB.getVersion(); + arangoDB.updateJwt("bla"); + + Throwable thrown = catchThrowable(arangoDB::getVersion); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException e = (ArangoDBException) thrown; + assertThat(e.getResponseCode()).isEqualTo(401); + + arangoDB.updateJwt(jwt); + arangoDB.getVersion(); + arangoDB.shutdown(); + } + + private ArangoDB.Builder getBuilder(Protocol protocol) { + return new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .useProtocol(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/shaded-integration-tests/src/test/jackson/java/com/arangodb/ParallelTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ParallelTest.java new file mode 100644 index 000000000..2df8079bb --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/ParallelTest.java @@ -0,0 +1,42 @@ +package com.arangodb; + +import com.arangodb.internal.config.FileConfigPropertiesProvider; +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.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +class ParallelTest { + + @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); + ArangoDB adb = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .useProtocol(protocol) + .maxConnections(1) + .build(); + + ExecutorService es = Executors.newFixedThreadPool(3); + List> tasks = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + tasks.add(es.submit(() -> adb.db().query("return sleep(1)", Void.class))); + } + + Thread.sleep(2_000); + assertThat(tasks).allMatch(Future::isDone); + adb.shutdown(); + es.shutdown(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/SerializableTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/SerializableTest.java new file mode 100644 index 000000000..c1e770530 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/SerializableTest.java @@ -0,0 +1,57 @@ +package com.arangodb; + +import com.arangodb.entity.BaseDocument; +import com.arangodb.entity.ErrorEntity; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import org.junit.jupiter.api.Test; + +import java.io.*; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SerializableTest { + + @Test + public void serializeBaseDocument() throws IOException, ClassNotFoundException { + BaseDocument bd = new BaseDocument("poaids"); + bd.setId("apdso/02193"); + bd.setRevision("poip"); + bd.addAttribute("aaa", "bbb"); + + BaseDocument bd2 = roundTrip(bd); + assertThat(bd).isEqualTo(bd2); + } + + @Test + public void serializeArangoDBException() throws IOException, ClassNotFoundException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jn = JsonNodeFactory.instance.objectNode() + .put("errorMessage", "boomError") + .put("exception", "boomException") + .put("code", 11) + .put("errorNum", 22); + ErrorEntity ee = mapper.readerFor(ErrorEntity.class).readValue(jn); + ArangoDBException e = new ArangoDBException(ee); + + ArangoDBException e2 = roundTrip(e); + assertThat(e2.getException()).isEqualTo(e.getException()); + assertThat(e2.getResponseCode()).isEqualTo(e.getResponseCode()); + assertThat(e2.getErrorNum()).isEqualTo(e.getErrorNum()); + assertThat(e2.getRequestId()).isEqualTo(e.getRequestId()); + } + + private T roundTrip(T input) throws IOException, ClassNotFoundException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(os); + objectOutputStream.writeObject(input); + + InputStream is = new ByteArrayInputStream(os.toByteArray()); + ObjectInputStream objectInputStream = new ObjectInputStream(is); + T output = (T) objectInputStream.readObject(); + objectInputStream.close(); + + return output; + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionConflictsTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionConflictsTest.java new file mode 100644 index 000000000..1e5ed62df --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionConflictsTest.java @@ -0,0 +1,119 @@ +/* + * 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 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 StreamTransactionConflictsTest 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("dbs") + void conflictOnInsertDocumentWithNotYetCommittedTx(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx1 = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + StreamTransactionEntity tx2 = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + String key = UUID.randomUUID().toString(); + + // insert a document from within tx1 + db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(key), new DocumentCreateOptions().streamTransactionId(tx1.getId())); + + // insert conflicting document from within tx2 + Throwable thrown = catchThrowable(() -> db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(key), + new DocumentCreateOptions().streamTransactionId(tx2.getId()))); + 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()); + db.abortStreamTransaction(tx2.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void conflictOnInsertDocumentWithAlreadyCommittedTx(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx1 = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + StreamTransactionEntity tx2 = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + String key = UUID.randomUUID().toString(); + + // insert a document from within tx1 + db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(key), new DocumentCreateOptions().streamTransactionId(tx1.getId())); + + // commit tx1 + db.commitStreamTransaction(tx1.getId()); + + // insert conflicting document from within tx2 + Throwable thrown = catchThrowable(() -> db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(key), + new DocumentCreateOptions().streamTransactionId(tx2.getId()))); + 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/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionGraphTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionGraphTest.java new file mode 100644 index 000000000..e4d73fa33 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionGraphTest.java @@ -0,0 +1,407 @@ +/* + * 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.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Michele Rastelli + */ +class StreamTransactionGraphTest 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 vertices() { + return dbsStream() + .map(db -> db.graph(GRAPH_NAME).vertexCollection(VERTEX_COLLECTION_1)) + .map(Arguments::of); + } + + private static Stream edges() { + return dbsStream() + .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, ArangoGraph graph) { + ArangoVertexCollection vertexCollection1 = graph.vertexCollection(VERTEX_COLLECTION_1); + ArangoVertexCollection vertexCollection2 = graph.vertexCollection(VERTEX_COLLECTION_2); + VertexEntity v1 = vertexCollection1.insertVertex(new BaseDocument(), + new VertexCreateOptions().streamTransactionId(streamTransactionId)); + VertexEntity v2 = vertexCollection2.insertVertex(new BaseDocument(), + new VertexCreateOptions().streamTransactionId(streamTransactionId)); + BaseEdgeDocument value = new BaseEdgeDocument(); + value.setFrom(v1.getId()); + value.setTo(v2.getId()); + return value; + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void getVertex(ArangoVertexCollection vertexCollection1) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoDatabase 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)); + + // insert a vertex from outside the tx + VertexEntity createdVertex = vertexCollection1.insertVertex(new BaseDocument()); + + // assert that the vertex is not found from within the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId()))).isNull(); + + db.abortStreamTransaction(tx.getId()); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void createVertex(ArangoVertexCollection vertexCollection1) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoDatabase 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)); + + // insert a vertex from within the tx + VertexEntity createdVertex = vertexCollection1.insertVertex(new BaseDocument(), + new VertexCreateOptions().streamTransactionId(tx.getId())); + + // assert that the vertex is not found from outside the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, null)).isNull(); + + // assert that the vertex is found from within the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId()))).isNotNull(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the vertex is found after commit + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, null)).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void replaceVertex(ArangoVertexCollection vertexCollection1) { + 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); + + ArangoDatabase 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)); + + // replace vertex from within the tx + doc.updateAttribute("test", "bar"); + vertexCollection1.replaceVertex(createdVertex.getKey(), doc, + new VertexReplaceOptions().streamTransactionId(tx.getId())); + + // assert that the vertex has not been replaced from outside the tx + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, null) + .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())).getProperties()).containsEntry("test" + , "bar"); + + db.commitStreamTransaction(tx.getId()); + + // assert that the vertex has been replaced after commit + assertThat(vertexCollection1.getVertex(createdVertex.getKey(), BaseDocument.class, null) + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void updateVertex(ArangoVertexCollection vertexCollection1) { + 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); + + ArangoDatabase 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)); + + // update vertex from within the tx + doc.updateAttribute("test", "bar"); + vertexCollection1.updateVertex(createdDoc.getKey(), doc, + new VertexUpdateOptions().streamTransactionId(tx.getId())); + + // assert that the vertex has not been updated from outside the tx + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, null) + .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())).getProperties()).containsEntry("test" + , "bar"); + + db.commitStreamTransaction(tx.getId()); + + // assert that the vertex has been updated after commit + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, null) + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("vertices") + void deleteVertex(ArangoVertexCollection vertexCollection1) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + VertexEntity createdDoc = vertexCollection1.insertVertex(new BaseDocument(), null); + + ArangoDatabase 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)); + + // delete vertex from within the tx + vertexCollection1.deleteVertex(createdDoc.getKey(), new VertexDeleteOptions().streamTransactionId(tx.getId())); + + // assert that the vertex has not been deleted from outside the tx + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, null)).isNotNull(); + + // assert that the vertex has been deleted from within the tx + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId()))).isNull(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the vertex has been deleted after commit + assertThat(vertexCollection1.getVertex(createdDoc.getKey(), BaseDocument.class, null)).isNull(); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("edges") + void getEdge(ArangoEdgeCollection edgeCollection) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoDatabase 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)); + + // insert an edge from outside the tx + EdgeEntity createdEdge = edgeCollection.insertEdge(createEdgeValue(null, edgeCollection.graph())); + + // assert that the edge is not found from within the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId()))).isNull(); + + db.abortStreamTransaction(tx.getId()); + } + + + @ParameterizedTest(name = "{index}") + @MethodSource("edges") + void createEdge(ArangoEdgeCollection edgeCollection) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoDatabase 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)); + + // insert an edge from within the tx + EdgeEntity createdEdge = edgeCollection.insertEdge(createEdgeValue(tx.getId(), edgeCollection.graph()), + new EdgeCreateOptions().streamTransactionId(tx.getId())); + + // assert that the edge is not found from outside the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, null)).isNull(); + + // assert that the edge is found from within the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId()))).isNotNull(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the edge is found after commit + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, null)).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("edges") + void replaceEdge(ArangoEdgeCollection edgeCollection) { + 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); + + ArangoDatabase 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)); + + // replace edge from within the tx + doc.updateAttribute("test", "bar"); + edgeCollection.replaceEdge(createdEdge.getKey(), doc, + new EdgeReplaceOptions().streamTransactionId(tx.getId())); + + // assert that the edge has not been replaced from outside the tx + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, null) + .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())).getProperties()).containsEntry("test" + , "bar"); + + db.commitStreamTransaction(tx.getId()); + + // assert that the edge has been replaced after commit + assertThat(edgeCollection.getEdge(createdEdge.getKey(), BaseEdgeDocument.class, null) + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("edges") + void updateEdge(ArangoEdgeCollection edgeCollection) { + 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); + + ArangoDatabase 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)); + + // update edge from within the tx + doc.updateAttribute("test", "bar"); + edgeCollection.updateEdge(createdDoc.getKey(), doc, new EdgeUpdateOptions().streamTransactionId(tx.getId())); + + // assert that the edge has not been updated from outside the tx + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, null) + .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())).getProperties()).containsEntry("test" + , "bar"); + + db.commitStreamTransaction(tx.getId()); + + // assert that the edge has been updated after commit + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, null) + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("edges") + void deleteEdge(ArangoEdgeCollection edgeCollection) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + EdgeEntity createdDoc = edgeCollection.insertEdge(createEdgeValue(null, edgeCollection.graph()), null); + + ArangoDatabase 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)); + + // delete edge from within the tx + edgeCollection.deleteEdge(createdDoc.getKey(), new EdgeDeleteOptions().streamTransactionId(tx.getId())); + + // assert that the edge has not been deleted from outside the tx + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, null)).isNotNull(); + + // assert that the edge has been deleted from within the tx + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, + new GraphDocumentReadOptions().streamTransactionId(tx.getId()))).isNull(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the edge has been deleted after commit + assertThat(edgeCollection.getEdge(createdDoc.getKey(), BaseEdgeDocument.class, null)).isNull(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionTest.java new file mode 100644 index 000000000..3b47d9222 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/StreamTransactionTest.java @@ -0,0 +1,820 @@ +/* + * 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.io.IOException; +import java.util.*; +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 StreamTransactionTest extends BaseJunit5 { + + private static final String COLLECTION_NAME = "StreamTransactionTest_collection"; + + @BeforeAll + static void init() { + initCollections(COLLECTION_NAME); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void beginStreamTransaction(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db.beginStreamTransaction(null); + assertThat(tx.getId()).isNotNull(); + assertThat(tx.getStatus()).isEqualTo(StreamTransactionStatus.running); + db.abortStreamTransaction(tx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void beginStreamTransactionWithNonExistingCollectionsShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + Throwable thrown = catchThrowable(() -> + db.beginStreamTransaction(new StreamTransactionOptions().writeCollections("notExistingCollection"))); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void abortStreamTransaction(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity begunTx = db.beginStreamTransaction(null); + StreamTransactionEntity abortedTx = db.abortStreamTransaction(begunTx.getId()); + + assertThat(abortedTx.getId()).isNotNull(); + assertThat(abortedTx.getId()).isEqualTo(begunTx.getId()); + assertThat(abortedTx.getStatus()).isEqualTo(StreamTransactionStatus.aborted); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void abortStreamTransactionTwice(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity begunTx = db.beginStreamTransaction(null); + db.abortStreamTransaction(begunTx.getId()); + db.abortStreamTransaction(begunTx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void abortStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + Throwable thrown = catchThrowable(() -> db.abortStreamTransaction("000000")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void abortStreamTransactionWithInvalidTransactionIdShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + Throwable thrown = catchThrowable(() -> db.abortStreamTransaction("invalidTransactionId")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void abortCommittedStreamTransactionShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null); + db.commitStreamTransaction(createdTx.getId()); + Throwable thrown = catchThrowable(() -> db.abortStreamTransaction(createdTx.getId())); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getStreamTransaction(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null); + StreamTransactionEntity gotTx = db.getStreamTransaction(createdTx.getId()); + + assertThat(gotTx.getId()).isNotNull(); + assertThat(gotTx.getId()).isEqualTo(createdTx.getId()); + assertThat(gotTx.getStatus()).isEqualTo(StreamTransactionStatus.running); + + db.abortStreamTransaction(createdTx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + Throwable thrown = catchThrowable(() -> db.getStreamTransaction("000000")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getStreamTransactionWithInvalidTransactionIdShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + Throwable thrown = catchThrowable(() -> db.getStreamTransaction("invalidTransactionId")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void commitStreamTransaction(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null); + StreamTransactionEntity committedTx = db.commitStreamTransaction(createdTx.getId()); + + assertThat(committedTx.getId()).isNotNull(); + assertThat(committedTx.getId()).isEqualTo(createdTx.getId()); + assertThat(committedTx.getStatus()).isEqualTo(StreamTransactionStatus.committed); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void commitStreamTransactionTwice(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null); + db.commitStreamTransaction(createdTx.getId()); + db.commitStreamTransaction(createdTx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void commitStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + Throwable thrown = catchThrowable(() -> db.commitStreamTransaction("000000")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void commitStreamTransactionWithInvalidTransactionIdShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + Throwable thrown = catchThrowable(() -> db.commitStreamTransaction("invalidTransactionId")); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void commitAbortedStreamTransactionShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity createdTx = db.beginStreamTransaction(null); + db.abortStreamTransaction(createdTx.getId()); + Throwable thrown = catchThrowable(() -> db.commitStreamTransaction(createdTx.getId())); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getDocument(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)); + ArangoCollection collection = db.collection(COLLECTION_NAME); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = collection + .insertDocument(new BaseDocument(), null); + + // assert that the document is not found from within the tx + assertThat(collection.getDocument(externalDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId()))).isNull(); + + db.abortStreamTransaction(tx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getDocumentWithNonExistingTransactionIdShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + + Throwable thrown = catchThrowable(() -> collection + .getDocument("docId", BaseDocument.class, new DocumentReadOptions().streamTransactionId("123456"))); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getDocumentWithInvalidTransactionIdShouldThrow(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + Throwable thrown = catchThrowable(() -> collection + .getDocument("docId", BaseDocument.class, new DocumentReadOptions().streamTransactionId("abcde"))); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void getDocuments(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)); + ArangoCollection collection = db.collection(COLLECTION_NAME); + + // insert documents from outside the tx + DocumentCreateEntity externalDoc1 = collection + .insertDocument(new BaseDocument(), null); + + DocumentCreateEntity externalDoc2 = collection + .insertDocument(new BaseDocument(), null); + + // 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())); + + assertThat(documents.getDocuments()).isEmpty(); + + db.abortStreamTransaction(tx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void insertDocument(ArangoDatabase 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 a document from within the tx + DocumentCreateEntity txDoc = collection + .insertDocument(new BaseDocument(), new DocumentCreateOptions().streamTransactionId(tx.getId())); + + // assert that the document is not found from outside the tx + assertThat(collection.getDocument(txDoc.getKey(), BaseDocument.class, null)).isNull(); + + // assert that the document is found from within the tx + assertThat(collection.getDocument(txDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId()))).isNotNull(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the document is found after commit + assertThat(collection.getDocument(txDoc.getKey(), BaseDocument.class, null)).isNotNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void insertDocuments(ArangoDatabase 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 + MultiDocumentEntity> txDocs = collection + .insertDocuments(Arrays.asList(new BaseDocument(), new BaseDocument(), new BaseDocument()), + new DocumentCreateOptions().streamTransactionId(tx.getId())); + + 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).getDocuments()).isEmpty(); + + // assert that the documents are found from within the tx + assertThat(collection + .getDocuments(keys, BaseDocument.class, new DocumentReadOptions().streamTransactionId(tx.getId())) + .getDocuments()).hasSize(keys.size()); + + db.commitStreamTransaction(tx.getId()); + + // assert that the document is found after commit + assertThat(collection.getDocuments(keys, BaseDocument.class, null).getDocuments()).hasSize(keys.size()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void replaceDocument(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("test", "foo"); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + DocumentCreateEntity createdDoc = collection.insertDocument(doc, null); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + // replace document from within the tx + doc.updateAttribute("test", "bar"); + collection.replaceDocument(createdDoc.getKey(), doc, + new DocumentReplaceOptions().streamTransactionId(tx.getId())); + + // assert that the document has not been replaced from outside the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null) + .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())).getProperties()).containsEntry("test", + "bar"); + + db.commitStreamTransaction(tx.getId()); + + // assert that the document has been replaced after commit + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null) + .getProperties()).containsEntry("test", "bar"); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void replaceDocuments(ArangoDatabase db) { + 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()); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + List createdDocs = collection + .insertDocuments(docs, new DocumentCreateOptions().returnNew(true)).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)); + + 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())); + + // assert that the documents has not been replaced from outside the tx + collection.getDocuments(keys, BaseDocument.class, null).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())) + .getDocuments().stream().map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("bar")); + + db.commitStreamTransaction(tx.getId()); + + // assert that the document has been replaced after commit + collection.getDocuments(keys, BaseDocument.class, null).getDocuments().stream() + .map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("bar")); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void updateDocument(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("test", "foo"); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + DocumentCreateEntity createdDoc = collection.insertDocument(doc, null); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + // 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) + .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())).getProperties()).containsEntry("test", "bar") + ; + + db.commitStreamTransaction(tx.getId()); + + // assert that the document has been updated after commit + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null) + .getProperties()).containsEntry("test", "bar"); + + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void updateDocuments(ArangoDatabase db) { + 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()); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + List createdDocs = collection + .insertDocuments(docs, new DocumentCreateOptions().returnNew(true)).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)); + + 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())); + + // assert that the documents have not been updated from outside the tx + collection.getDocuments(keys, BaseDocument.class, null).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())) + .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).getDocuments().stream() + .map(it -> ((String) it.getAttribute("test"))) + .forEach(it -> assertThat(it).isEqualTo("bar")); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void deleteDocument(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + DocumentCreateEntity createdDoc = collection + .insertDocument(new BaseDocument(), null); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + // delete document from within the tx + collection + .deleteDocument(createdDoc.getKey(), new DocumentDeleteOptions().streamTransactionId(tx.getId())); + + // assert that the document has not been deleted from outside the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null)).isNotNull(); + + // assert that the document has been deleted from within the tx + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId()))).isNull(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the document has been deleted after commit + assertThat(collection.getDocument(createdDoc.getKey(), BaseDocument.class, null)).isNull(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void deleteDocuments(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + List keys = collection + .insertDocuments(Arrays.asList(new BaseDocument(), new BaseDocument(), new BaseDocument())) + .getDocuments().stream().map(DocumentEntity::getKey).collect(Collectors.toList()); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + // delete document from within the tx + collection + .deleteDocuments(keys, new DocumentDeleteOptions().streamTransactionId(tx.getId())); + + // assert that the documents has not been deleted from outside the tx + assertThat(collection.getDocuments(keys, BaseDocument.class, null).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())) + .getDocuments()).isEmpty(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the document has been deleted after commit + assertThat(collection.getDocuments(keys, BaseDocument.class, null).getDocuments()).isEmpty(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void documentExists(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)); + ArangoCollection collection = db.collection(COLLECTION_NAME); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = collection + .insertDocument(new BaseDocument(), null); + + // assert that the document is not found from within the tx + assertThat(collection + .documentExists(externalDoc.getKey(), new DocumentExistsOptions().streamTransactionId(tx.getId()))).isFalse(); + + db.abortStreamTransaction(tx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void count(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + Long initialCount = collection.count().getCount(); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)); + + // insert a document from outside the tx + collection.insertDocument(new BaseDocument(), null); + + // assert that the document is not counted from within the tx + assertThat(collection.count(new CollectionCountOptions().streamTransactionId(tx.getId())) + .getCount()).isEqualTo(initialCount); + + db.abortStreamTransaction(tx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void truncate(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + collection.insertDocument(new BaseDocument(), null); + + StreamTransactionEntity tx = db.beginStreamTransaction( + new StreamTransactionOptions().readCollections(COLLECTION_NAME).writeCollections(COLLECTION_NAME)); + + // truncate document from within the tx + collection.truncate(new CollectionTruncateOptions().streamTransactionId(tx.getId())); + + // assert that the collection has not been truncated from outside the tx + assertThat(collection.count().getCount()).isPositive(); + + // assert that the collection has been truncated from within the tx + assertThat(collection.count(new CollectionCountOptions().streamTransactionId(tx.getId())) + .getCount()).isZero(); + + db.commitStreamTransaction(tx.getId()); + + // assert that the collection has been truncated after commit + assertThat(collection.count().getCount()).isZero(); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void createCursor(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)); + ArangoCollection collection = db.collection(COLLECTION_NAME); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = collection + .insertDocument(new BaseDocument(), null); + + 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", bindVars, + new AqlQueryOptions().streamTransactionId(tx.getId()), BaseDocument.class); + + // assert that the document is not found from within the tx + assertThat(cursor.hasNext()).isFalse(); + + db.abortStreamTransaction(tx.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void nextCursor(ArangoDatabase 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", bindVars, + new AqlQueryOptions().streamTransactionId(tx.getId()).batchSize(2), BaseDocument.class); + + 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("dbs") + void getStreamTransactions(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx1 = db.beginStreamTransaction(null); + StreamTransactionEntity tx2 = db.beginStreamTransaction(null); + + List createdIds = Arrays.asList(tx1.getId(), tx2.getId()); + Set gotTxs = db.getStreamTransactions().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()); + db.abortStreamTransaction(tx2.getId()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionAllowImplicitFalse(ArangoDatabase db) { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().allowImplicit(false)); + ArangoCollection collection = db.collection(COLLECTION_NAME); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = collection + .insertDocument(new BaseDocument(), null); + + // assert that we cannot read from collection + Throwable thrown = catchThrowable(() -> collection.getDocument(externalDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId()))); + 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()); + } + + @ParameterizedTest(name = "{index}") + @MethodSource("dbs") + void transactionDirtyRead(ArangoDatabase db) throws IOException { + assumeTrue(isCluster()); + assumeTrue(isAtLeastVersion(3, 10)); + + ArangoCollection collection = db.collection(COLLECTION_NAME); + DocumentCreateEntity doc = collection.insertDocument(new BaseDocument()); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions() + .readCollections(COLLECTION_NAME) + .allowDirtyRead(true)); + + MultiDocumentEntity readDocs = collection.getDocuments(Collections.singletonList(doc.getKey()), + BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())); + + assertThat(readDocs.isPotentialDirtyRead()).isTrue(); + assertThat(readDocs.getDocuments()).hasSize(1); + + final ArangoCursor cursor = db.query("FOR i IN @@col RETURN i", + Collections.singletonMap("@col", COLLECTION_NAME), + new AqlQueryOptions().streamTransactionId(tx.getId()), BaseDocument.class); + assertThat(cursor.isPotentialDirtyRead()).isTrue(); + cursor.close(); + + db.abortStreamTransaction(tx.getId()); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/annotations/AnnotatedEntity.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/annotations/AnnotatedEntity.java new file mode 100644 index 000000000..993fff0ec --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/annotations/AnnotatedEntity.java @@ -0,0 +1,81 @@ +package com.arangodb.annotations; + +import com.arangodb.serde.jackson.*; + +import java.util.Objects; + +public class AnnotatedEntity { + + @Id + private String id; + + @Key + private String key; + + @Rev + private String rev; + + @From + private String from; + + @To + private String to; + + public AnnotatedEntity() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getRev() { + return rev; + } + + public void setRev(String rev) { + this.rev = rev; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AnnotatedEntity that = (AnnotatedEntity) o; + return Objects.equals(getId(), that.getId()) && Objects.equals(getKey(), that.getKey()) && Objects + .equals(getRev(), that.getRev()) && Objects.equals(getFrom(), that.getFrom()) && Objects + .equals(getTo(), that.getTo()); + } + + @Override + public int hashCode() { + return Objects.hash(getId(), getKey(), getRev(), getFrom(), getTo()); + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/annotations/ArangoAnnotationsTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/annotations/ArangoAnnotationsTest.java new file mode 100644 index 000000000..e3363f3c0 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/annotations/ArangoAnnotationsTest.java @@ -0,0 +1,64 @@ +/* + * 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.annotations; + +import com.arangodb.ContentType; +import com.arangodb.serde.ArangoSerde; +import com.arangodb.serde.jackson.JacksonSerdeProvider; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Michele Rastelli + */ +class ArangoAnnotationsTest { + + @ParameterizedTest + @EnumSource(ContentType.class) + void documentFieldAnnotations(ContentType contentType) { + ArangoSerde mapper = new JacksonSerdeProvider().of(contentType); + + AnnotatedEntity e = new AnnotatedEntity(); + e.setId("Id"); + e.setKey("Key"); + e.setRev("Rev"); + e.setFrom("From"); + e.setTo("To"); + + byte[] serialized = mapper.serialize(e); + Map deserialized = mapper.deserialize(serialized, Map.class); + assertThat(deserialized) + .containsEntry("_id", e.getId()) + .containsEntry("_key", e.getKey()) + .containsEntry("_rev", e.getRev()) + .containsEntry("_from", e.getFrom()) + .containsEntry("_to", e.getTo()) + .hasSize(5); + + AnnotatedEntity deserializedEntity = mapper.deserialize(serialized, AnnotatedEntity.class); + assertThat(deserializedEntity).isEqualTo(e); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoCollectionTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoCollectionTest.java new file mode 100644 index 000000000..720b1d261 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoCollectionTest.java @@ -0,0 +1,2514 @@ +/* + * 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.async; + +import com.arangodb.ArangoDBException; +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.JacksonSerde; +import com.arangodb.serde.jackson.Key; +import com.arangodb.shaded.fasterxml.jackson.databind.JsonNode; +import com.arangodb.util.RawData; +import com.arangodb.util.RawJson; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +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; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoCollectionTest extends BaseTest { + + private static final String COLLECTION_NAME = "db_collection_test"; + + ArangoCollectionTest() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + if (!collection.exists().get()) { + collection.create().get(); + } + } + + @BeforeAll + static void setup() throws InterruptedException, ExecutionException { + db.createCollection(COLLECTION_NAME, null).get(); + } + + @AfterEach + void teardown() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void create() throws InterruptedException, ExecutionException { + final CollectionEntity result = db.collection(COLLECTION_NAME + "_1").create().get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + db.collection(COLLECTION_NAME + "_1").drop().get(); + } + + @Test + void insertDocument() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), null) + .whenComplete((doc, ex) -> { + assertThat(ex).isNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNull(); + assertThat(doc.getId()).isEqualTo(COLLECTION_NAME + "/" + doc.getKey()); + }) + .get(); + } + + @Test + void insertDocumentReturnNew() throws InterruptedException, ExecutionException { + final DocumentCreateOptions options = new DocumentCreateOptions().returnNew(true); + db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), options) + .whenComplete((doc, ex) -> { + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNotNull(); + }) + .get(); + } + + @Test + void insertDocumentWithTypeOverwriteModeReplace() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 7)); + assumeTrue(db.getSerde().getUserSerde() instanceof JacksonSerde, "polymorphic deserialization support " + + "required"); + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + 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).get(); + 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"); + } + + @Test + void insertDocumentWaitForSync() throws InterruptedException, ExecutionException { + final DocumentCreateOptions options = new DocumentCreateOptions().waitForSync(true); + db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), options) + .whenComplete((doc, ex) -> { + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + assertThat(doc.getNew()).isNull(); + }) + .get(); + } + + @Test + void insertDocumentAsJson() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME) + .insertDocument(RawJson.of("{\"_key\":\"docRaw\",\"a\":\"test\"}"), null) + .whenComplete((doc, ex) -> { + assertThat(doc).isNotNull(); + assertThat(doc.getId()).isNotNull(); + assertThat(doc.getKey()).isNotNull(); + assertThat(doc.getRev()).isNotNull(); + }) + .get(); + } + + @Test + void getDocument() throws InterruptedException, ExecutionException { + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + db.collection(COLLECTION_NAME).getDocument(createResult.getKey(), BaseDocument.class, null) + .whenComplete((readResult, ex) -> { + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + }) + .get(); + } + + @Test + void getDocumentIfMatch() throws InterruptedException, ExecutionException { + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifMatch(createResult.getRev()); + db.collection(COLLECTION_NAME).getDocument(createResult.getKey(), BaseDocument.class, options) + .whenComplete((readResult, ex) -> { + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + }) + .get(); + } + + @Test + void getDocumentIfMatchFail() throws InterruptedException, ExecutionException { + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifMatch("no"); + db.collection(COLLECTION_NAME).getDocument(createResult.getKey(), BaseDocument.class, options) + .whenComplete((doc, ex) -> assertThat(doc).isNull()) + .get(); + } + + @Test + void getDocumentIfNoneMatch() throws InterruptedException, ExecutionException { + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifNoneMatch("no"); + db.collection(COLLECTION_NAME).getDocument(createResult.getKey(), BaseDocument.class, options) + .whenComplete((readResult, ex) -> { + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getId()).isEqualTo(COLLECTION_NAME + "/" + createResult.getKey()); + }) + .get(); + } + + @Test + void getDocumentIfNoneMatchFail() throws InterruptedException, ExecutionException { + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), null).get(); + assertThat(createResult.getKey()).isNotNull(); + final DocumentReadOptions options = new DocumentReadOptions().ifNoneMatch(createResult.getRev()); + db.collection(COLLECTION_NAME).getDocument(createResult.getKey(), BaseDocument.class, options) + .whenComplete((doc, ex) -> assertThat(doc).isNull()) + .get(); + } + + @Test + void getDocumentAsJson() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).insertDocument(RawJson.of("{\"_key\":\"docRaw\",\"a\":\"test\"}")).get(); + db.collection(COLLECTION_NAME).getDocument("docRaw", RawJson.class) + .whenComplete((readResult, ex) -> { + assertThat(readResult.getValue().contains("\"_key\":\"docRaw\"")).isEqualTo(true); + assertThat(readResult.getValue().contains("\"_id\":\"db_collection_test/docRaw\"")).isEqualTo(true); + }) + .get(); + } + + @Test + void getDocumentNotFound() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).getDocument("no", BaseDocument.class) + .whenComplete((doc, ex) -> assertThat(doc).isNull()) + .get(); + } + + @Test + void getDocumentWrongKey() { + Throwable thrown = catchThrowable(() -> db.collection(COLLECTION_NAME).getDocument("no/no", + BaseDocument.class)); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @Test + void getDocuments() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("3")); + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final MultiDocumentEntity documents = db.collection(COLLECTION_NAME) + .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"); + } + } + + @Test + void getDocumentsNotFound() throws InterruptedException, ExecutionException { + final MultiDocumentEntity readResult = db.collection(COLLECTION_NAME) + .getDocuments(Collections.singleton("no"), BaseDocument.class).get(); + assertThat(readResult).isNotNull(); + assertThat(readResult.getDocuments()).isEmpty(); + assertThat(readResult.getErrors()).hasSize(1); + } + + @Test + void getDocumentsWrongKey() throws InterruptedException, ExecutionException { + final MultiDocumentEntity readResult = db.collection(COLLECTION_NAME) + .getDocuments(Collections.singleton("no/no"), BaseDocument.class).get(); + assertThat(readResult).isNotNull(); + assertThat(readResult.getDocuments()).isEmpty(); + assertThat(readResult.getErrors()).hasSize(1); + } + + @Test + void updateDocument() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + + final CompletableFuture> f = db.collection(COLLECTION_NAME) + .updateDocument(createResult.getKey(), doc, null); + f.whenComplete((updateResult, ex) -> { + 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()); + }).get(); + final DocumentUpdateEntity updateResult = f.get(); + + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .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"); + } + + @Test + void updateDocumentWithDifferentReturnType() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + final String key = "key-" + UUID.randomUUID(); + final BaseDocument doc = new BaseDocument(key); + doc.addAttribute("a", "test"); + collection.insertDocument(doc).get(); + + 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"); + } + + @Test + void updateDocumentIfMatch() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).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 CompletableFuture> f = db.collection(COLLECTION_NAME) + .updateDocument(createResult.getKey(), doc, options); + assertThat(f).isNotNull(); + f.whenComplete((updateResult, ex) -> { + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + }).get(); + final DocumentUpdateEntity updateResult = f.get(); + + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .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"); + } + + @Test + void updateDocumentIfMatchFail() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + try { + final DocumentUpdateOptions options = new DocumentUpdateOptions().ifMatch("no"); + db.collection(COLLECTION_NAME).updateDocument(createResult.getKey(), doc, options).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void updateDocumentReturnNew() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + final DocumentUpdateOptions options = new DocumentUpdateOptions().returnNew(true); + db.collection(COLLECTION_NAME).updateDocument(createResult.getKey(), doc, options) + .whenComplete((updateResult, ex) -> { + 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"); + }) + .get(); + } + + @Test + void updateDocumentReturnOld() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + final DocumentUpdateOptions options = new DocumentUpdateOptions().returnOld(true); + db.collection(COLLECTION_NAME).updateDocument(createResult.getKey(), doc, options) + .whenComplete((updateResult, ex) -> { + 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"); + }) + .get(); + } + + @Test + void updateDocumentKeepNullTrue() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.updateAttribute("a", null); + final DocumentUpdateOptions options = new DocumentUpdateOptions().keepNull(true); + db.collection(COLLECTION_NAME).updateDocument(createResult.getKey(), doc, options) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + }) + .get(); + + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .getDocument(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @Test + void updateDocumentKeepNullFalse() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.updateAttribute("a", null); + final DocumentUpdateOptions options = new DocumentUpdateOptions().keepNull(false); + db.collection(COLLECTION_NAME).updateDocument(createResult.getKey(), doc, options) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + }) + .get(); + + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .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"); + } + + @Test + void updateDocumentMergeObjectsTrue() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final Map a = new HashMap<>(); + a.put("a", "test"); + doc.addAttribute("a", a); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + a.clear(); + a.put("b", "test"); + doc.updateAttribute("a", a); + final DocumentUpdateOptions options = new DocumentUpdateOptions().mergeObjects(true); + db.collection(COLLECTION_NAME).updateDocument(createResult.getKey(), doc, options) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + }) + .get(); + + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .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).containsKey("a"); + assertThat(aMap).containsKey("b"); + } + + @Test + void updateDocumentMergeObjectsFalse() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final Map a = new HashMap<>(); + a.put("a", "test"); + doc.addAttribute("a", a); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + a.clear(); + a.put("b", "test"); + doc.updateAttribute("a", a); + final DocumentUpdateOptions options = new DocumentUpdateOptions().mergeObjects(false); + db.collection(COLLECTION_NAME).updateDocument(createResult.getKey(), doc, options) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult).isNotNull(); + assertThat(updateResult.getId()).isEqualTo(createResult.getId()); + assertThat(updateResult.getRev()).isNotEqualTo(updateResult.getOldRev()); + assertThat(updateResult.getOldRev()).isEqualTo(createResult.getRev()); + }) + .get(); + + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .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"); + } + + @Test + void updateDocumentIgnoreRevsFalse() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.setRevision("no"); + try { + final DocumentUpdateOptions options = new DocumentUpdateOptions().ignoreRevs(false); + db.collection(COLLECTION_NAME).updateDocument(createResult.getKey(), doc, options).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void replaceDocument() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final CompletableFuture> f = db.collection(COLLECTION_NAME) + .replaceDocument(createResult.getKey(), doc, null); + f.whenComplete((replaceResult, ex) -> { + 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()); + }).get(); + final DocumentUpdateEntity replaceResult = f.get(); + + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .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"); + } + + @Test + void replaceDocumentIfMatch() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().ifMatch(createResult.getRev()); + final CompletableFuture> f = db.collection(COLLECTION_NAME) + .replaceDocument(createResult.getKey(), doc, options); + f.whenComplete((replaceResult, ex) -> { + assertThat(replaceResult).isNotNull(); + assertThat(replaceResult.getId()).isEqualTo(createResult.getId()); + assertThat(replaceResult.getRev()).isNotEqualTo(replaceResult.getOldRev()); + assertThat(replaceResult.getOldRev()).isEqualTo(createResult.getRev()); + }).get(); + final DocumentUpdateEntity replaceResult = f.get(); + + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .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"); + } + + @Test + void replaceDocumentIfMatchFail() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + try { + final DocumentReplaceOptions options = new DocumentReplaceOptions().ifMatch("no"); + db.collection(COLLECTION_NAME).replaceDocument(createResult.getKey(), doc, options).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void replaceDocumentIgnoreRevsFalse() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + doc.setRevision("no"); + try { + final DocumentReplaceOptions options = new DocumentReplaceOptions().ignoreRevs(false); + db.collection(COLLECTION_NAME).replaceDocument(createResult.getKey(), doc, options).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void replaceDocumentReturnNew() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().returnNew(true); + db.collection(COLLECTION_NAME).replaceDocument(createResult.getKey(), doc, options) + .whenComplete((replaceResult, ex) -> { + 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"); + }) + .get(); + } + + @Test + void replaceDocumentReturnOld() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final DocumentReplaceOptions options = new DocumentReplaceOptions().returnOld(true); + db.collection(COLLECTION_NAME).replaceDocument(createResult.getKey(), doc, options) + .whenComplete((replaceResult, ex) -> { + 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"); + }) + .get(); + } + + @Test + void deleteDocument() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + db.collection(COLLECTION_NAME).deleteDocument(createResult.getKey()).get(); + db.collection(COLLECTION_NAME).getDocument(createResult.getKey(), BaseDocument.class, null) + .whenComplete((document, ex) -> assertThat(document).isNull()) + .get(); + } + + @Test + void deleteDocumentReturnOld() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + final DocumentDeleteOptions options = new DocumentDeleteOptions().returnOld(true); + db.collection(COLLECTION_NAME).deleteDocument(createResult.getKey(), options, BaseDocument.class) + .whenComplete((deleteResult, ex) -> { + 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"); + }) + .get(); + } + + @Test + void deleteDocumentIfMatch() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + final DocumentDeleteOptions options = new DocumentDeleteOptions().ifMatch(createResult.getRev()); + db.collection(COLLECTION_NAME).deleteDocument(createResult.getKey(), options).get(); + db.collection(COLLECTION_NAME).getDocument(createResult.getKey(), BaseDocument.class, null) + .whenComplete((document, ex) -> assertThat(document).isNull()) + .get(); + } + + @Test + void deleteDocumentIfMatchFail() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME).insertDocument(doc, null) + .get(); + final DocumentDeleteOptions options = new DocumentDeleteOptions().ifMatch("no"); + try { + db.collection(COLLECTION_NAME).deleteDocument(createResult.getKey(), options).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void getIndex() throws InterruptedException, ExecutionException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = db.collection(COLLECTION_NAME).ensureHashIndex(fields, null).get(); + db.collection(COLLECTION_NAME).getIndex(createResult.getId()) + .whenComplete((readResult, ex) -> { + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getType()).isEqualTo(createResult.getType()); + }) + .get(); + } + + @Test + void getIndexByKey() throws InterruptedException, ExecutionException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = db.collection(COLLECTION_NAME).ensureHashIndex(fields, null).get(); + db.collection(COLLECTION_NAME).getIndex(createResult.getId().split("/")[1]) + .whenComplete((readResult, ex) -> { + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getType()).isEqualTo(createResult.getType()); + }) + .get(); + } + + @Test + void deleteIndex() throws InterruptedException, ExecutionException { + final Collection fields = new ArrayList<>(); + fields.add("deleteIndexField"); + final IndexEntity createResult = db.collection(COLLECTION_NAME).ensureHashIndex(fields, null).get(); + db.getIndex(createResult.getId()).get(); + db.collection(COLLECTION_NAME).deleteIndex(createResult.getId()) + .whenComplete((id, ex) -> { + assertThat(id).isEqualTo(createResult.getId()); + try { + db.getIndex(id).get(); + fail(); + } catch (final InterruptedException exception) { + exception.printStackTrace(); + fail(); + } catch (final ExecutionException exception) { + assertThat(exception.getCause()).isInstanceOf(ArangoDBException.class); + } + }) + .get(); + } + + @Test + void deleteIndexByKey() throws InterruptedException, ExecutionException { + final Collection fields = new ArrayList<>(); + fields.add("deleteIndexByKeyField"); + final IndexEntity createResult = db.collection(COLLECTION_NAME).ensureHashIndex(fields, null).get(); + db.getIndex(createResult.getId()).get(); + db.collection(COLLECTION_NAME).deleteIndex(createResult.getId().split("/")[1]) + .whenComplete((id, ex) -> { + assertThat(id).isEqualTo(createResult.getId()); + try { + db.getIndex(id).get(); + fail(); + } catch (final InterruptedException exception) { + exception.printStackTrace(); + fail(); + } catch (final ExecutionException exception) { + assertThat(exception.getCause()).isInstanceOf(ArangoDBException.class); + } + }) + .get(); + } + + @Test + void createHashIndex() throws InterruptedException, ExecutionException { + final boolean singleServer = isSingleServer(); + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + db.collection(COLLECTION_NAME).ensureHashIndex(fields, null) + .whenComplete((indexResult, ex) -> { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + assertThat(indexResult.getGeoJson()).isNull(); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isEqualTo(true); + assertThat(indexResult.getMinLength()).isNull(); + if (singleServer) { + assertThat(indexResult.getSelectivityEstimate()).isEqualTo(1.0); + } + assertThat(indexResult.getSparse()).isEqualTo(false); + assertThat(indexResult.getType()).isEqualTo(IndexType.hash); + assertThat(indexResult.getUnique()).isEqualTo(false); + }) + .get(); + } + + @Test + void createHashIndexWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final HashIndexOptions options = new HashIndexOptions(); + options.name("myHashIndex"); + + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureHashIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + if (isSingleServer()) { + assertThat(indexResult.getSelectivityEstimate()).isEqualTo(1.); + } + assertThat(indexResult.getSparse()).isFalse(); + assertThat(indexResult.getType()).isEqualTo(IndexType.hash); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getName()).isEqualTo("myHashIndex"); + } + + @Test + void createGeoIndex() throws InterruptedException, ExecutionException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + db.collection(COLLECTION_NAME).ensureGeoIndex(fields, null) + .whenComplete((indexResult, ex) -> { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getGeoJson()).isEqualTo(false); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isEqualTo(true); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSelectivityEstimate()).isNull(); + assertThat(indexResult.getSparse()).isEqualTo(true); + assertThat(indexResult.getType()).isIn(IndexType.geo, IndexType.geo1); + assertThat(indexResult.getUnique()).isEqualTo(false); + }) + .get(); + } + + @Test + void createGeoIndexWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final GeoIndexOptions options = new GeoIndexOptions(); + options.name("myGeoIndex1"); + + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureGeoIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains("a"); + 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("myGeoIndex1"); + } + + @Test + void createGeo2Index() throws ExecutionException, InterruptedException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + db.collection(COLLECTION_NAME).ensureGeoIndex(fields, null).whenComplete((indexResult, ex) -> { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isEqualTo(true); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isEqualTo(true); + assertThat(indexResult.getUnique()).isEqualTo(false); + try { + if (isAtLeastVersion(3, 4)) { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo); + } else { + assertThat(indexResult.getType()).isEqualTo(IndexType.geo2); + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + fail(); + } + }).get(); + } + + @Test + void createGeo2IndexWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final GeoIndexOptions options = new GeoIndexOptions(); + options.name("myGeoIndex2"); + + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureGeoIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + 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("myGeoIndex2"); + } + + @Test + void createSkiplistIndex() throws InterruptedException, ExecutionException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + db.collection(COLLECTION_NAME).ensureSkiplistIndex(fields, null) + .whenComplete((indexResult, ex) -> { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + assertThat(indexResult.getGeoJson()).isNull(); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isEqualTo(true); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isEqualTo(false); + assertThat(indexResult.getType()).isEqualTo(IndexType.skiplist); + assertThat(indexResult.getUnique()).isEqualTo(false); + }) + .get(); + } + + @Test + void createSkiplistIndexWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final SkiplistIndexOptions options = new SkiplistIndexOptions(); + options.name("mySkiplistIndex"); + + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureSkiplistIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isFalse(); + assertThat(indexResult.getType()).isEqualTo(IndexType.skiplist); + assertThat(indexResult.getUnique()).isFalse(); + assertThat(indexResult.getName()).isEqualTo("mySkiplistIndex"); + } + + @Test + void createPersistentIndex() throws InterruptedException, ExecutionException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + db.collection(COLLECTION_NAME).ensurePersistentIndex(fields, null) + .whenComplete((indexResult, ex) -> { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + assertThat(indexResult.getGeoJson()).isNull(); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isEqualTo(true); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getSparse()).isEqualTo(false); + assertThat(indexResult.getType()).isEqualTo(IndexType.persistent); + assertThat(indexResult.getUnique()).isEqualTo(false); + assertThat(indexResult.getDeduplicate()).isTrue(); + }) + .get(); + } + + @Test + void createPersistentIndexWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final PersistentIndexOptions options = new PersistentIndexOptions(); + options.name("myPersistentIndex"); + + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensurePersistentIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + 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("myPersistentIndex"); + } + + @Test + void indexDeduplicate() 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 = db.collection(COLLECTION_NAME).ensurePersistentIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getDeduplicate()).isTrue(); + } + + @Test + void indexDeduplicateFalse() 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 = db.collection(COLLECTION_NAME).ensurePersistentIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getDeduplicate()).isFalse(); + } + + @Test + void createFulltextIndex() throws InterruptedException, ExecutionException { + final Collection fields = new ArrayList<>(); + fields.add("a"); + db.collection(COLLECTION_NAME).ensureFulltextIndex(fields, null) + .whenComplete((indexResult, ex) -> { + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getGeoJson()).isNull(); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isEqualTo(true); + assertThat(indexResult.getSelectivityEstimate()).isNull(); + assertThat(indexResult.getSparse()).isEqualTo(true); + assertThat(indexResult.getType()).isEqualTo(IndexType.fulltext); + assertThat(indexResult.getUnique()).isEqualTo(false); + }) + .get(); + } + + @Test + void createFulltextIndexWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final FulltextIndexOptions options = new FulltextIndexOptions(); + options.name("myFulltextIndex"); + + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureFulltextIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + 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("myFulltextIndex"); + } + + @Test + void createTtlIndexWithoutOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final Collection fields = new ArrayList<>(); + fields.add("a"); + try { + db.collection(COLLECTION_NAME).ensureTtlIndex(fields, null).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + assertThat(e.getCause().getMessage()).contains("expireAfter attribute must be a number"); + assertThat(((ArangoDBException) e.getCause()).getResponseCode()).isEqualTo(400); + assertThat(((ArangoDBException) e.getCause()).getErrorNum()).isEqualTo(10); + } + } + + @Test + void createTtlIndexWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final Collection fields = new ArrayList<>(); + fields.add("a"); + + final TtlIndexOptions options = new TtlIndexOptions(); + options.name("myTtlIndex"); + options.expireAfter(3600); + + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureTtlIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getFields()).contains("a"); + 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("myTtlIndex"); + } + + @Test + void createZKDIndex() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 9)); + db.collection(COLLECTION_NAME).truncate().get(); + + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureZKDIndex(fields, null).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + assertThat(indexResult.getGeoJson()).isNull(); + assertThat(indexResult.getId()).startsWith(COLLECTION_NAME); + assertThat(indexResult.getIsNewlyCreated()).isTrue(); + assertThat(indexResult.getMinLength()).isNull(); + assertThat(indexResult.getType()).isEqualTo(IndexType.zkd); + assertThat(indexResult.getUnique()).isFalse(); + db.collection(COLLECTION_NAME).deleteIndex(indexResult.getId()).get(); + } + + @Test + void createZKDIndexWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 9)); + db.collection(COLLECTION_NAME).truncate().get(); + + final ZKDIndexOptions options = new ZKDIndexOptions() + .name("myZKDIndex") + .fieldValueTypes(ZKDIndexOptions.FieldValueTypes.DOUBLE); + + final Collection fields = new ArrayList<>(); + fields.add("a"); + fields.add("b"); + final IndexEntity indexResult = db.collection(COLLECTION_NAME).ensureZKDIndex(fields, options).get(); + assertThat(indexResult).isNotNull(); + assertThat(indexResult.getConstraint()).isNull(); + assertThat(indexResult.getFields()).contains("a"); + assertThat(indexResult.getFields()).contains("b"); + 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("myZKDIndex"); + db.collection(COLLECTION_NAME).deleteIndex(indexResult.getId()).get(); + } + + @Test + void getIndexes() throws InterruptedException, ExecutionException { + final int initialIndexCount = db.collection(COLLECTION_NAME).getIndexes().get().size(); + final Collection fields = new ArrayList<>(); + fields.add("a"); + db.collection(COLLECTION_NAME).ensureHashIndex(fields, null).get(); + db.collection(COLLECTION_NAME).getIndexes() + .whenComplete((indexes, ex) -> { + assertThat(indexes).isNotNull(); + assertThat(indexes.size()).isEqualTo(initialIndexCount + 1); + for (final IndexEntity i : indexes) { + if (i.getType() == IndexType.hash) { + assertThat(i.getFields().size()).isEqualTo(1); + assertThat(i.getFields()).contains("a"); + } + } + }) + .get(); + } + + @Test + void exists() throws InterruptedException, ExecutionException { + assertThat(db.collection(COLLECTION_NAME).exists().get()).isTrue(); + assertThat(db.collection(COLLECTION_NAME + "no").exists().get()).isFalse(); + } + + @Test + void truncate() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + db.collection(COLLECTION_NAME).insertDocument(doc, null).get(); + final BaseDocument readResult = db.collection(COLLECTION_NAME) + .getDocument(doc.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(doc.getKey()); + db.collection(COLLECTION_NAME).truncate() + .whenComplete((truncateResult, ex) -> { + assertThat(truncateResult).isNotNull(); + assertThat(truncateResult.getId()).isNotNull(); + }) + .get(); + final BaseDocument document = db.collection(COLLECTION_NAME).getDocument(doc.getKey(), BaseDocument.class, null) + .get(); + assertThat(document).isNull(); + } + + @Test + void getCount() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).count() + .whenComplete((countEmpty, ex) -> { + assertThat(countEmpty).isNotNull(); + assertThat(countEmpty.getCount()).isEqualTo(0L); + }) + .get(); + + db.collection(COLLECTION_NAME).insertDocument(RawJson.of("{}"), null).get(); + + db.collection(COLLECTION_NAME).count() + .whenComplete((count, ex) -> assertThat(count.getCount()).isEqualTo(1L)) + .get(); + } + + @Test + void documentExists() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).documentExists("no", null) + .whenComplete((existsNot, ex) -> assertThat(existsNot).isEqualTo(false)) + .get(); + + db.collection(COLLECTION_NAME).insertDocument(RawJson.of("{\"_key\":\"abc\"}")).get(); + + db.collection(COLLECTION_NAME).documentExists("abc", null) + .whenComplete((exists, ex) -> assertThat(exists).isEqualTo(true)) + .get(); + } + + @Test + void documentExistsIfMatch() throws InterruptedException, ExecutionException { + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME) + .insertDocument(RawJson.of("{\"_key\":\"abc\"}")).get(); + final DocumentExistsOptions options = new DocumentExistsOptions().ifMatch(createResult.getRev()); + db.collection(COLLECTION_NAME).documentExists("abc", options) + .whenComplete((exists, ex) -> assertThat(exists).isEqualTo(true)) + .get(); + } + + @Test + void documentExistsIfMatchFail() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).insertDocument(RawJson.of("{\"_key\":\"abc\"}")).get(); + final DocumentExistsOptions options = new DocumentExistsOptions().ifMatch("no"); + db.collection(COLLECTION_NAME).documentExists("abc", options) + .whenComplete((exists, ex) -> assertThat(exists).isEqualTo(false)) + .get(); + } + + @Test + void documentExistsIfNoneMatch() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).insertDocument(RawJson.of("{\"_key\":\"abc\"}")).get(); + final DocumentExistsOptions options = new DocumentExistsOptions().ifNoneMatch("no"); + db.collection(COLLECTION_NAME).documentExists("abc", options) + .whenComplete((exists, ex) -> assertThat(exists).isEqualTo(true)) + .get(); + } + + @Test + void documentExistsIfNoneMatchFail() throws InterruptedException, ExecutionException { + final DocumentCreateEntity createResult = db.collection(COLLECTION_NAME) + .insertDocument(RawJson.of("{\"_key\":\"abc\"}")).get(); + final DocumentExistsOptions options = new DocumentExistsOptions().ifNoneMatch(createResult.getRev()); + db.collection(COLLECTION_NAME).documentExists("abc", options) + .whenComplete((exists, ex) -> assertThat(exists).isEqualTo(false)) + .get(); + } + + @Test + void insertDocuments() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + db.collection(COLLECTION_NAME).insertDocuments(values) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments().size()).isEqualTo(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void insertDocumentsOne() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + db.collection(COLLECTION_NAME).insertDocuments(values) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments().size()).isEqualTo(1); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void insertDocumentsEmpty() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + db.collection(COLLECTION_NAME).insertDocuments(values) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments().size()).isEqualTo(0); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void insertDocumentsReturnNew() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + final DocumentCreateOptions options = new DocumentCreateOptions().returnNew(true); + db.collection(COLLECTION_NAME).insertDocuments(values, options) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments().size()).isEqualTo(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors().size()).isEqualTo(0); + for (final DocumentCreateEntity doc : docs.getDocuments()) { + assertThat(doc.getNew()).isNotNull(); + final BaseDocument baseDocument = doc.getNew(); + assertThat(baseDocument.getKey()).isNotNull(); + } + }) + .get(); + } + + @Test + void insertDocumentsFail() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("2")); + db.collection(COLLECTION_NAME).insertDocuments(values) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments().size()).isEqualTo(2); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors().size()).isEqualTo(1); + assertThat(docs.getErrors().iterator().next().getErrorNum()).isEqualTo(1210); + }) + .get(); + } + + @Test + void insertDocumentsRawData() throws ExecutionException, InterruptedException { + final RawData values = RawData.of("[{},{},{}]"); + final MultiDocumentEntity docs = db.collection(COLLECTION_NAME).insertDocuments(values).get(); + assertThat(docs).isNotNull(); + assertThat(docs.getDocuments()).isNotNull(); + assertThat(docs.getDocuments()).hasSize(3); + assertThat(docs.getErrors()).isNotNull(); + assertThat(docs.getErrors()).isEmpty(); + } + + @Test + void insertDocumentsRawDataReturnNew() throws ExecutionException, InterruptedException { + final RawData values = RawData.of("[{\"aaa\":33},{\"aaa\":33},{\"aaa\":33}]"); + final MultiDocumentEntity> docs = + db.collection(COLLECTION_NAME).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).getValue()); + assertThat(jn.has("aaa")).isTrue(); + assertThat(jn.get("aaa").intValue()).isEqualTo(33); + } + } + + @Test + void importDocuments() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + values.add(new BaseDocument()); + db.collection(COLLECTION_NAME).importDocuments(values) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(values.size()); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(0); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsDuplicateDefaultError() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("2")); + db.collection(COLLECTION_NAME).importDocuments(values) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsDuplicateError() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("2")); + db.collection(COLLECTION_NAME).importDocuments(values, +new DocumentImportOptions().onDuplicate(OnDuplicate.error)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsDuplicateIgnore() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("2")); + db.collection(COLLECTION_NAME).importDocuments(values, + new DocumentImportOptions().onDuplicate(OnDuplicate.ignore)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(0); + assertThat(docs.getIgnored()).isEqualTo(1); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsDuplicateReplace() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("2")); + db.collection(COLLECTION_NAME).importDocuments(values, +new DocumentImportOptions().onDuplicate(OnDuplicate.replace)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(0); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(1); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsDuplicateUpdate() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("2")); + db.collection(COLLECTION_NAME).importDocuments(values, + new DocumentImportOptions().onDuplicate(OnDuplicate.update)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(0); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(1); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsCompleteFail() throws InterruptedException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("2")); + try { + db.collection(COLLECTION_NAME).importDocuments(values, new DocumentImportOptions().complete(true)).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) e.getCause()).getErrorNum()).isEqualTo(1210); + } + } + + @Test + void importDocumentsDetails() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + values.add(new BaseDocument("2")); + db.collection(COLLECTION_NAME).importDocuments(values, new DocumentImportOptions().details(true)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails().size()).isEqualTo(1); + assertThat(docs.getDetails().iterator().next()).contains("unique constraint violated"); + }) + .get(); + } + + @Test + void importDocumentsOverwriteFalse() throws InterruptedException, ExecutionException { + final ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + collection.insertDocument(new BaseDocument()).get(); + assertThat(collection.count().get().getCount()).isEqualTo(1L); + + 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(3L); + } + + @Test + void importDocumentsOverwriteTrue() throws InterruptedException, ExecutionException { + final ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + collection.insertDocument(new BaseDocument()).get(); + assertThat(collection.count().get().getCount()).isEqualTo(1L); + + 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); + } + + @Test + void importDocumentsFromToPrefix() throws InterruptedException, ExecutionException { + db.createCollection(COLLECTION_NAME + "_edge", new CollectionCreateOptions().type(CollectionType.EDGES)).get(); + final ArangoCollectionAsync collection = db.collection(COLLECTION_NAME + "_edge"); + try { + final Collection values = new ArrayList<>(); + final String[] keys = {"1", "2"}; + for (String s : keys) { + values.add(new BaseEdgeDocument(s, "from", "to")); + } + assertThat(values).hasSize(keys.length); + + final DocumentImportEntity importResult = collection + .importDocuments(values, new DocumentImportOptions().fromPrefix("foo").toPrefix("bar")).get(); + assertThat(importResult).isNotNull(); + assertThat(importResult.getCreated()).isEqualTo(values.size()); + for (String key : keys) { + BaseEdgeDocument doc; + doc = collection.getDocument(key, BaseEdgeDocument.class).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getFrom()).isEqualTo("foo/from"); + assertThat(doc.getTo()).isEqualTo("bar/to"); + } + } finally { + collection.drop().get(); + } + } + + @Test + void importDocumentsJson() throws InterruptedException, ExecutionException { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"}]"; + db.collection(COLLECTION_NAME).importDocuments(RawData.of(values)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(0); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsJsonDuplicateDefaultError() throws InterruptedException, ExecutionException { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + db.collection(COLLECTION_NAME).importDocuments(RawData.of(values)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsJsonDuplicateError() throws InterruptedException, ExecutionException { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + db.collection(COLLECTION_NAME).importDocuments(RawData.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.error)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsJsonDuplicateIgnore() throws InterruptedException, ExecutionException { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + db.collection(COLLECTION_NAME).importDocuments(RawData.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.ignore)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(0); + assertThat(docs.getIgnored()).isEqualTo(1); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsJsonDuplicateReplace() throws InterruptedException, ExecutionException { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + db.collection(COLLECTION_NAME).importDocuments(RawData.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.replace)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(0); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(1); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsJsonDuplicateUpdate() throws InterruptedException, ExecutionException { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + db.collection(COLLECTION_NAME).importDocuments(RawData.of(values), + new DocumentImportOptions().onDuplicate(OnDuplicate.update)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(0); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(1); + assertThat(docs.getDetails()).isEmpty(); + }) + .get(); + } + + @Test + void importDocumentsJsonCompleteFail() throws InterruptedException { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + try { + db.collection(COLLECTION_NAME).importDocuments(RawData.of(values), + new DocumentImportOptions().complete(true)).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) e.getCause()).getErrorNum()).isEqualTo(1210); + } + } + + @Test + void importDocumentsJsonDetails() throws InterruptedException, ExecutionException { + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"},{\"_key\":\"2\"}]"; + db.collection(COLLECTION_NAME).importDocuments(RawData.of(values), new DocumentImportOptions().details(true)) + .whenComplete((docs, ex) -> { + assertThat(docs).isNotNull(); + assertThat(docs.getCreated()).isEqualTo(2); + assertThat(docs.getEmpty()).isEqualTo(0); + assertThat(docs.getErrors()).isEqualTo(1); + assertThat(docs.getIgnored()).isEqualTo(0); + assertThat(docs.getUpdated()).isEqualTo(0); + assertThat(docs.getDetails().size()).isEqualTo(1); + assertThat(docs.getDetails().iterator().next()).contains("unique constraint violated"); + }) + .get(); + } + + @Test + void importDocumentsJsonOverwriteFalse() throws InterruptedException, ExecutionException { + final ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + collection.insertDocument(new BaseDocument()).get(); + assertThat(collection.count().get().getCount()).isEqualTo(1L); + + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"}]"; + collection.importDocuments(RawData.of(values), new DocumentImportOptions().overwrite(false)).get(); + assertThat(collection.count().get().getCount()).isEqualTo(3L); + } + + @Test + void importDocumentsJsonOverwriteTrue() throws InterruptedException, ExecutionException { + final ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + collection.insertDocument(new BaseDocument()).get(); + assertThat(collection.count().get().getCount()).isEqualTo(1L); + + final String values = "[{\"_key\":\"1\"},{\"_key\":\"2\"}]"; + collection.importDocuments(RawData.of(values), new DocumentImportOptions().overwrite(true)).get(); + assertThat(collection.count().get().getCount()).isEqualTo(2L); + } + + @Test + void importDocumentsJsonFromToPrefix() throws InterruptedException, ExecutionException { + db.createCollection(COLLECTION_NAME + "_edge", new CollectionCreateOptions().type(CollectionType.EDGES)).get(); + final ArangoCollectionAsync collection = db.collection(COLLECTION_NAME + "_edge"); + try { + final String[] keys = {"1", "2"}; + final String values = "[{\"_key\":\"1\",\"_from\":\"from\",\"_to\":\"to\"},{\"_key\":\"2\"," + + "\"_from\":\"from\",\"_to\":\"to\"}]"; + + final DocumentImportEntity importResult = collection + .importDocuments(RawData.of(values), + new DocumentImportOptions().fromPrefix("foo").toPrefix("bar")).get(); + assertThat(importResult).isNotNull(); + assertThat(importResult.getCreated()).isEqualTo(2); + for (String key : keys) { + BaseEdgeDocument doc; + doc = collection.getDocument(key, BaseEdgeDocument.class).get(); + assertThat(doc).isNotNull(); + assertThat(doc.getFrom()).isEqualTo("foo/from"); + assertThat(doc.getTo()).isEqualTo("bar/to"); + } + } finally { + collection.drop().get(); + } + } + + @Test + void deleteDocumentsByKey() throws InterruptedException, ExecutionException { + 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); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection keys = new ArrayList<>(); + keys.add("1"); + keys.add("2"); + db.collection(COLLECTION_NAME).deleteDocuments(keys) + .whenComplete((deleteResult, ex) -> { + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments().size()).isEqualTo(2); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isIn("1", "2"); + } + assertThat(deleteResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void deleteDocumentsByDocuments() throws InterruptedException, ExecutionException { + 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); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + db.collection(COLLECTION_NAME).deleteDocuments(values) + .whenComplete((deleteResult, ex) -> { + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments().size()).isEqualTo(2); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isIn("1", "2"); + } + assertThat(deleteResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void deleteDocumentsByKeyOne() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection keys = new ArrayList<>(); + keys.add("1"); + db.collection(COLLECTION_NAME).deleteDocuments(keys) + .whenComplete((deleteResult, ex) -> { + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments().size()).isEqualTo(1); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isEqualTo("1"); + } + assertThat(deleteResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void deleteDocumentsByDocumentOne() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + db.collection(COLLECTION_NAME).deleteDocuments(values) + .whenComplete((deleteResult, ex) -> { + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments().size()).isEqualTo(1); + for (final DocumentDeleteEntity i : deleteResult.getDocuments()) { + assertThat(i.getKey()).isEqualTo("1"); + } + assertThat(deleteResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void deleteDocumentsEmpty() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection keys = new ArrayList<>(); + db.collection(COLLECTION_NAME).deleteDocuments(keys) + .whenComplete((deleteResult, ex) -> { + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments().size()).isEqualTo(0); + assertThat(deleteResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void deleteDocumentsByKeyNotExisting() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection keys = new ArrayList<>(); + keys.add("1"); + keys.add("2"); + db.collection(COLLECTION_NAME).deleteDocuments(keys) + .whenComplete((deleteResult, ex) -> { + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments().size()).isEqualTo(0); + assertThat(deleteResult.getErrors().size()).isEqualTo(2); + }) + .get(); + } + + @Test + void deleteDocumentsByDocumentsNotExisting() throws InterruptedException, ExecutionException { + 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); + } + db.collection(COLLECTION_NAME).deleteDocuments(values) + .whenComplete((deleteResult, ex) -> { + assertThat(deleteResult).isNotNull(); + assertThat(deleteResult.getDocuments().size()).isEqualTo(0); + assertThat(deleteResult.getErrors().size()).isEqualTo(2); + }) + .get(); + } + + @Test + void deleteDocumentsRawDataByKeyReturnOld() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + final RawData values = RawData.of("[{\"_key\":\"1\"},{\"_key\":\"2\"}]"); + collection.insertDocuments(values).get(); + final RawData keys = RawData.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()).getValue()); + assertThat(jn.get("_key").asText()).isEqualTo(i.getKey()); + } + assertThat(deleteResult.getErrors()).isEmpty(); + } + + @Test + void updateDocuments() throws InterruptedException, ExecutionException { + 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); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection updatedValues = new ArrayList<>(); + for (final BaseDocument i : values) { + i.addAttribute("a", "test"); + updatedValues.add(i); + } + db.collection(COLLECTION_NAME).updateDocuments(updatedValues) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult.getDocuments().size()).isEqualTo(2); + assertThat(updateResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void updateDocumentsWithDifferentReturnType() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + 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") && it.getAttribute("b").equals("test"))) + .isTrue(); + } + + @Test + void updateDocumentsOne() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection updatedValues = new ArrayList<>(); + final BaseDocument first = values.iterator().next(); + first.addAttribute("a", "test"); + updatedValues.add(first); + db.collection(COLLECTION_NAME).updateDocuments(updatedValues) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult.getDocuments().size()).isEqualTo(1); + assertThat(updateResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void updateDocumentsEmpty() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + db.collection(COLLECTION_NAME).updateDocuments(values) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult.getDocuments().size()).isEqualTo(0); + assertThat(updateResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void updateDocumentsWithoutKey() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + { + values.add(new BaseDocument("1")); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection updatedValues = new ArrayList<>(); + for (final BaseDocument i : values) { + i.addAttribute("a", "test"); + updatedValues.add(i); + } + updatedValues.add(new BaseDocument()); + db.collection(COLLECTION_NAME).updateDocuments(updatedValues) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult.getDocuments().size()).isEqualTo(1); + assertThat(updateResult.getErrors().size()).isEqualTo(1); + }) + .get(); + } + + @Test + void updateDocumentsRawData() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + final RawData values = RawData.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values).get(); + + final RawData updatedValues = RawData.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(); + } + + @Test + void updateDocumentsRawDataReturnNew() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + final RawData values = RawData.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values).get(); + + final RawData updatedValues = RawData.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).getValue()); + assertThat(jn.has("foo")).isTrue(); + assertThat(jn.get("foo").textValue()).isEqualTo("bar"); + } + } + + @Test + void replaceDocuments() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + { + values.add(new BaseDocument("1")); + values.add(new BaseDocument("2")); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection updatedValues = new ArrayList<>(); + for (final BaseDocument i : values) { + i.addAttribute("a", "test"); + updatedValues.add(i); + } + db.collection(COLLECTION_NAME).replaceDocuments(updatedValues) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult.getDocuments().size()).isEqualTo(2); + assertThat(updateResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void replaceDocumentsOne() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + { + final BaseDocument e = new BaseDocument(UUID.randomUUID().toString()); + e.setKey("1"); + values.add(e); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection updatedValues = new ArrayList<>(); + final BaseDocument first = values.iterator().next(); + first.addAttribute("a", "test"); + updatedValues.add(first); + db.collection(COLLECTION_NAME).updateDocuments(updatedValues) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult.getDocuments().size()).isEqualTo(1); + assertThat(updateResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void replaceDocumentsEmpty() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + db.collection(COLLECTION_NAME).updateDocuments(values) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult.getDocuments().size()).isEqualTo(0); + assertThat(updateResult.getErrors().size()).isEqualTo(0); + }) + .get(); + } + + @Test + void replaceDocumentsWithoutKey() throws InterruptedException, ExecutionException { + final Collection values = new ArrayList<>(); + { + values.add(new BaseDocument("1")); + } + db.collection(COLLECTION_NAME).insertDocuments(values).get(); + final Collection updatedValues = new ArrayList<>(); + for (final BaseDocument i : values) { + i.addAttribute("a", "test"); + updatedValues.add(i); + } + updatedValues.add(new BaseDocument()); + db.collection(COLLECTION_NAME).updateDocuments(updatedValues) + .whenComplete((updateResult, ex) -> { + assertThat(updateResult.getDocuments().size()).isEqualTo(1); + assertThat(updateResult.getErrors().size()).isEqualTo(1); + }) + .get(); + } + + @Test + void replaceDocumentsRawData() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + final RawData values = RawData.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values).get(); + + final RawData updatedValues = RawData.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(); + } + + @Test + void replaceDocumentsRawDataReturnNew() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + final RawData values = RawData.of("[{\"_key\":\"1\"}, {\"_key\":\"2\"}]"); + collection.insertDocuments(values).get(); + + final RawData updatedValues = RawData.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).getValue()); + assertThat(jn.has("foo")).isTrue(); + assertThat(jn.get("foo").textValue()).isEqualTo("bar"); + } + } + + @Test + void getInfo() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).getInfo() + .whenComplete((result, ex) -> assertThat(result.getName()).isEqualTo(COLLECTION_NAME)) + .get(); + } + + @Test + void getPropeties() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).getProperties() + .whenComplete((result, ex) -> { + assertThat(result.getName()).isEqualTo(COLLECTION_NAME); + assertThat(result.getCount()).isNull(); + }) + .get(); + } + + @Test + void changeProperties() throws InterruptedException, ExecutionException { + final String collection = COLLECTION_NAME + "_prop"; + try { + db.createCollection(collection).get(); + final CollectionPropertiesEntity properties = db.collection(collection).getProperties().get(); + assertThat(properties.getWaitForSync()).isNotNull(); + final CollectionPropertiesOptions options = new CollectionPropertiesOptions(); + options.waitForSync(!properties.getWaitForSync()); + db.collection(collection).changeProperties(options) + .whenComplete((changedProperties, ex) -> { + assertThat(changedProperties.getWaitForSync()).isNotNull(); + assertThat(changedProperties.getWaitForSync()).isNotEqualTo(properties.getWaitForSync()); + }) + .get(); + } finally { + db.collection(collection).drop().get(); + } + } + + @Test + void rename() throws InterruptedException, ExecutionException { + assumeTrue(isSingleServer()); + db.collection(COLLECTION_NAME).rename(COLLECTION_NAME + "1") + .whenComplete((result, ex) -> { + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME + "1"); + }) + .get(); + final CollectionEntity info = db.collection(COLLECTION_NAME + "1").getInfo().get(); + assertThat(info.getName()).isEqualTo(COLLECTION_NAME + "1"); + try { + db.collection(COLLECTION_NAME).getInfo().get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + db.collection(COLLECTION_NAME + "1").rename(COLLECTION_NAME).get(); + } + + @Test + void responsibleShard() throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + assumeTrue(isAtLeastVersion(3, 5)); + ShardEntity shard = db.collection(COLLECTION_NAME).getResponsibleShard(new BaseDocument("testKey")).get(); + assertThat(shard).isNotNull(); + assertThat(shard.getShardId()).isNotNull(); + } + + @Test + void getRevision() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).getRevision() + .whenComplete((result, ex) -> { + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo(COLLECTION_NAME); + assertThat(result.getRevision()).isNotNull(); + }) + .get(); + } + + @Test + void grantAccessRW() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.collection(COLLECTION_NAME).grantAccess("user1", Permissions.RW).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void grantAccessRO() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.collection(COLLECTION_NAME).grantAccess("user1", Permissions.RO).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void grantAccessNONE() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.collection(COLLECTION_NAME).grantAccess("user1", Permissions.NONE).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void grantAccessUserNotFound() { + Throwable thrown = + catchThrowable(() -> db.collection(COLLECTION_NAME).grantAccess("user1", Permissions.RW).get()); + assertThat(thrown).isInstanceOf(ExecutionException.class); + } + + @Test + void revokeAccess() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.collection(COLLECTION_NAME).grantAccess("user1", Permissions.NONE).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void revokeAccessUserNotFound() { + Throwable thrown = + catchThrowable(() -> db.collection(COLLECTION_NAME).grantAccess("user1", Permissions.NONE).get()); + assertThat(thrown).isInstanceOf(ExecutionException.class); + } + + @Test + void resetAccess() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.collection(COLLECTION_NAME).resetAccess("user1").get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void resetAccessUserNotFound() { + Throwable thrown = catchThrowable(() -> db.collection(COLLECTION_NAME).resetAccess("user1").get()); + assertThat(thrown).isInstanceOf(ExecutionException.class); + } + + @Test + void getPermissions() throws InterruptedException, ExecutionException { + assertThat(db.collection(COLLECTION_NAME).getPermissions("root").get()).isEqualTo(Permissions.RW); + } + + @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; + } + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoDBTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoDBTest.java new file mode 100644 index 000000000..19f77ca5c --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoDBTest.java @@ -0,0 +1,566 @@ +/* + * 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.async; + +import com.arangodb.*; +import com.arangodb.entity.*; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import com.arangodb.internal.serde.SerdeUtils; +import com.arangodb.model.*; +import com.arangodb.util.RawJson; +import com.arangodb.util.TestUtils; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoDBTest { + + private static final String ROOT = "root"; + private static final String USER = "mit dem mund"; + private static final String PW = "machts der hund"; + private static Boolean extendedNames; + + private final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + private final ArangoDB arangoDBSync = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + + private boolean isEnterprise() { + return arangoDBSync.getVersion().getLicense() == License.ENTERPRISE; + } + + private boolean isCluster() { + return arangoDBSync.getRole() == ServerRole.COORDINATOR; + } + + private boolean isAtLeastVersion(final int major, final int minor) { + return TestUtils.isAtLeastVersion(arangoDBSync.getVersion().getVersion(), major, minor, 0); + } + + private boolean isLessThanVersion(final int major, final int minor) { + return TestUtils.isLessThanVersion(arangoDBSync.getVersion().getVersion(), major, minor, 0); + } + + private boolean supportsExtendedNames() { + if (extendedNames == null) { + try { + ArangoDatabase testDb = arangoDBSync.db(DbName.of("test-" + TestUtils.generateRandomDbName(20, true))); + testDb.create(); + extendedNames = true; + testDb.drop(); + } catch (ArangoDBException e) { + extendedNames = false; + } + } + return extendedNames; + } + + @Test + void getVersion() throws InterruptedException, ExecutionException { + arangoDB.getVersion() + .whenComplete((version, ex) -> { + assertThat(version).isNotNull(); + assertThat(version.getServer()).isNotNull(); + assertThat(version.getVersion()).isNotNull(); + }) + .get(); + } + + @Test + @Timeout(2) + void nestedGetVersion() { + for (int i = 0; i < 100; i++) { + try { + arangoDB.getVersion() + .whenComplete((v1, ex1) -> { + assertThat(v1).isNotNull(); + assertThat(v1.getServer()).isNotNull(); + assertThat(v1.getVersion()).isNotNull(); + try { + arangoDB.getVersion() + .whenComplete((v2, ex2) -> { + assertThat(v2).isNotNull(); + assertThat(v2.getServer()).isNotNull(); + assertThat(v2.getVersion()).isNotNull(); + try { + arangoDB.getVersion() + .whenComplete((v3, ex3) -> { + assertThat(v3).isNotNull(); + assertThat(v3.getServer()).isNotNull(); + assertThat(v3.getVersion()).isNotNull(); + }) + .get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + fail(); + } + }) + .get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + fail(); + } + }) + .get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + fail(); + } + } + } + + @Test + void createDatabase() throws InterruptedException, ExecutionException { + arangoDB.createDatabase(BaseTest.TEST_DB) + .whenComplete((result, ex) -> assertThat(result).isEqualTo(true)) + .get(); + arangoDB.db(BaseTest.TEST_DB).drop().get(); + } + + @Test + void createDatabaseWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + assumeTrue(isAtLeastVersion(3, 6)); + + final DbName dbName = DbName.of("testDB-" + TestUtils.generateRandomDbName(20, supportsExtendedNames())); + final Boolean resultCreate = arangoDB.createDatabase(new DBCreateOptions() + .name(dbName) + .options(new DatabaseOptions() + .writeConcern(2) + .replicationFactor(2) + .sharding("") + ) + ).get(); + assertThat(resultCreate).isTrue(); + + DatabaseEntity info = arangoDB.db(dbName).getInfo().get(); + assertThat(info.getReplicationFactor().getValue()).isEqualTo(2); + assertThat(info.getWriteConcern()).isEqualTo(2); + assertThat(info.getSharding()).isEmpty(); + + final Boolean resultDelete = arangoDB.db(dbName).drop().get(); + assertThat(resultDelete).isTrue(); + } + + @Test + void createDatabaseWithOptionsSatellite() throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 6)); + + final DbName dbName = DbName.of("testDB-" + TestUtils.generateRandomDbName(20, supportsExtendedNames())); + final Boolean resultCreate = arangoDB.createDatabase(new DBCreateOptions() + .name(dbName) + .options(new DatabaseOptions() + .writeConcern(2) + .replicationFactor(ReplicationFactor.ofSatellite()) + .sharding("") + ) + ).get(); + assertThat(resultCreate).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(); + } + + @Test + void deleteDatabase() throws InterruptedException, ExecutionException { + final Boolean resultCreate = arangoDB.createDatabase(BaseTest.TEST_DB).get(); + assertThat(resultCreate).isTrue(); + arangoDB.db(BaseTest.TEST_DB).drop() + .whenComplete((resultDelete, ex) -> assertThat(resultDelete).isEqualTo(true)) + .get(); + } + + @Test + void getDatabases() throws InterruptedException, ExecutionException { + Collection dbs = arangoDB.getDatabases().get(); + assertThat(dbs).isNotNull(); + assertThat(dbs).isNotEmpty(); + final int dbCount = dbs.size(); + assertThat(dbs).contains("_system"); + arangoDB.createDatabase(BaseTest.TEST_DB).get(); + dbs = arangoDB.getDatabases().get(); + assertThat(dbs).hasSizeGreaterThan(dbCount); + assertThat(dbs).contains("_system"); + assertThat(dbs).contains(BaseTest.TEST_DB.get()); + arangoDB.db(BaseTest.TEST_DB).drop().get(); + } + + @Test + void getAccessibleDatabases() throws InterruptedException, ExecutionException { + arangoDB.getAccessibleDatabases() + .whenComplete((dbs, ex) -> { + assertThat(dbs).isNotNull(); + assertThat(dbs).isNotEmpty(); + assertThat(dbs).contains("_system"); + }) + .get(); + } + + @Test + void getAccessibleDatabasesFor() throws InterruptedException, ExecutionException { + arangoDB.getAccessibleDatabasesFor("root") + .whenComplete((dbs, ex) -> { + assertThat(dbs).isNotNull(); + assertThat(dbs).isNotNull(); + assertThat(dbs).isNotEmpty(); + assertThat(dbs).contains("_system"); + }) + .get(); + } + + @Test + void createUser() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser(USER, PW, null) + .whenComplete((result, ex) -> { + assertThat(result).isNotNull(); + assertThat(result.getUser()).isEqualTo(USER); + }) + .get(); + } finally { + arangoDB.deleteUser(USER).get(); + } + } + + @Test + void deleteUser() throws InterruptedException, ExecutionException { + arangoDB.createUser(USER, PW, null).get(); + arangoDB.deleteUser(USER).get(); + } + + @Test + void getUserRoot() throws InterruptedException, ExecutionException { + arangoDB.getUser(ROOT) + .whenComplete((user, ex) -> { + assertThat(user).isNotNull(); + assertThat(user.getUser()).isEqualTo(ROOT); + }) + .get(); + } + + @Test + void getUser() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser(USER, PW, null).get(); + arangoDB.getUser(USER) + .whenComplete((user, ex) -> assertThat(user.getUser()).isEqualTo(USER)) + .get(); + } finally { + arangoDB.deleteUser(USER).get(); + } + + } + + @Test + void getUsersOnlyRoot() throws InterruptedException, ExecutionException { + arangoDB.getUsers() + .whenComplete((users, ex) -> { + assertThat(users).isNotNull(); + assertThat(users).isNotEmpty(); + }) + .get(); + } + + @Test + void getUsers() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser(USER, PW, null).get(); + arangoDB.getUsers() + .whenComplete((users, ex) -> { + assertThat(users).isNotNull(); + assertThat(users).hasSizeGreaterThanOrEqualTo(2); + assertThat( + users.stream().map(UserEntity::getUser).collect(Collectors.toList()) + ).contains(ROOT, USER); + }) + .get(); + } finally { + arangoDB.deleteUser(USER).get(); + } + } + + @Test + void updateUserNoOptions() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser(USER, PW, null).get(); + arangoDB.updateUser(USER, null).get(); + } finally { + arangoDB.deleteUser(USER).get(); + } + } + + @Test + void updateUser() throws InterruptedException, ExecutionException { + try { + final Map extra = new HashMap<>(); + extra.put("hund", false); + arangoDB.createUser(USER, PW, new UserCreateOptions().extra(extra)).get(); + extra.put("hund", true); + extra.put("mund", true); + { + arangoDB.updateUser(USER, new UserUpdateOptions().extra(extra)) + .whenComplete((user, ex) -> { + assertThat(user).isNotNull(); + assertThat(user.getExtra()).hasSize(2); + assertThat(user.getExtra().get("hund")).isNotNull(); + assertThat(Boolean.valueOf(String.valueOf(user.getExtra().get("hund")))).isTrue(); + }) + .get(); + } + arangoDB.getUser(USER) + .whenComplete((user2, ex) -> { + assertThat(user2.getExtra()).hasSize(2); + assertThat(user2.getExtra().get("hund")).isNotNull(); + assertThat(Boolean.valueOf(String.valueOf(user2.getExtra().get("hund")))).isTrue(); + }) + .get(); + } finally { + arangoDB.deleteUser(USER).get(); + } + } + + @Test + void replaceUser() throws InterruptedException, ExecutionException { + try { + final Map extra = new HashMap<>(); + extra.put("hund", false); + arangoDB.createUser(USER, PW, new UserCreateOptions().extra(extra)).get(); + extra.remove("hund"); + extra.put("mund", true); + { + arangoDB.replaceUser(USER, new UserUpdateOptions().extra(extra)) + .whenComplete((user, ex) -> { + assertThat(user).isNotNull(); + assertThat(user.getExtra()).hasSize(1); + assertThat(user.getExtra().get("mund")).isNotNull(); + assertThat(Boolean.valueOf(String.valueOf(user.getExtra().get("mund")))).isTrue(); + }) + .get(); + } + { + arangoDB.getUser(USER) + .whenComplete((user2, ex) -> { + assertThat(user2.getExtra()).hasSize(1); + assertThat(user2.getExtra().get("mund")).isNotNull(); + assertThat(Boolean.valueOf(String.valueOf(user2.getExtra().get("mund")))).isTrue(); + }) + .get(); + } + } finally { + arangoDB.deleteUser(USER).get(); + } + } + + @Test + void updateUserDefaultDatabaseAccess() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser(USER, PW).get(); + arangoDB.grantDefaultDatabaseAccess(USER, Permissions.RW).get(); + } finally { + arangoDB.deleteUser(USER).get(); + } + } + + @Test + void updateUserDefaultCollectionAccess() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser(USER, PW).get(); + arangoDB.grantDefaultCollectionAccess(USER, Permissions.RW).get(); + } finally { + arangoDB.deleteUser(USER).get(); + } + } + + @Test + void authenticationFailPassword() throws InterruptedException { + final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .password("no").jwt(null).build(); + try { + arangoDB.getVersion().get(); + fail(); + } catch (final ExecutionException exception) { + assertThat(exception.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void authenticationFailUser() throws InterruptedException { + final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .user("no").jwt(null).build(); + try { + arangoDB.getVersion().get(); + fail(); + } catch (final ExecutionException exception) { + assertThat(exception.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void execute() throws InterruptedException, ExecutionException { + arangoDB + .execute(Request.builder() + .db(DbName.SYSTEM) + .method(Request.Method.GET) + .path("/_api/version") + .build(), RawJson.class) + .whenComplete((response, ex) -> { + assertThat(response.getBody()).isNotNull(); + assertThat(SerdeUtils.INSTANCE.parseJson(response.getBody().getValue()).get("version").isTextual()).isTrue(); + }) + .get(); + } + + @Test + void execute_acquireHostList_enabled() throws InterruptedException, ExecutionException { + final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .acquireHostList(true).build(); + arangoDB + .execute(Request.builder() + .db(DbName.SYSTEM) + .method(Request.Method.GET) + .path("/_api/version") + .build(), RawJson.class) + .whenComplete((response, ex) -> { + assertThat(response.getBody()).isNotNull(); + assertThat(SerdeUtils.INSTANCE.parseJson(response.getBody().getValue()).get("version").isTextual()).isTrue(); + }) + .get(); + } + + @Test + void getLogEntries() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 8)); + arangoDB.getLogEntries(null) + .whenComplete((logs, ex) -> { + assertThat(logs).isNotNull(); + assertThat(logs.getTotal()).isPositive(); + assertThat((long) logs.getMessages().size()).isEqualTo(logs.getTotal()); + }) + .get(); + } + + @Test + void getLogLevel() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 7)); // it fails in 3.6 active-failover (BTS-362) + arangoDB.getLogLevel() + .whenComplete((logLevel, ex) -> { + assertThat(logLevel).isNotNull(); + assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.INFO); + }) + .get(); + } + + @Test + void setLogLevel() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 7)); // it fails in 3.6 active-failover (BTS-362) + final LogLevelEntity entity = new LogLevelEntity(); + try { + entity.setAgency(LogLevelEntity.LogLevel.ERROR); + arangoDB.setLogLevel(entity) + .whenComplete((logLevel, ex) -> { + assertThat(logLevel).isNotNull(); + assertThat(logLevel.getAgency()).isEqualTo(LogLevelEntity.LogLevel.ERROR); + }) + .get(); + } finally { + entity.setAgency(LogLevelEntity.LogLevel.INFO); + arangoDB.setLogLevel(entity).get(); + } + } + + @Test + @Disabled("Manual execution only") + void queueTime() throws InterruptedException, ExecutionException { + List>> reqs = IntStream.range(0, 80) + .mapToObj(__ -> arangoDB.db().query("RETURN SLEEP(1)", Void.class)) + .collect(Collectors.toList()); + for (CompletableFuture> req : reqs) { + req.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]).isNotNull(); + assertThat(values[i].value).isGreaterThanOrEqualTo(0.0); + if (i > 0) { + assertThat(values[i].timestamp).isGreaterThanOrEqualTo(values[i - 1].timestamp); + } + } + assertThat(avg).isGreaterThan(0.0); + } else { + assertThat(avg).isEqualTo(0.0); + assertThat(values).isEmpty(); + } + } + + @Test + void getQueryOptimizerRules() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + final Collection rules = arangoDB.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(); + } + } + + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoDatabaseTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoDatabaseTest.java new file mode 100644 index 000000000..a12bf84ec --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoDatabaseTest.java @@ -0,0 +1,1024 @@ +/* + * 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.async; + +import com.arangodb.ArangoDBException; +import com.arangodb.DbName; +import com.arangodb.entity.AqlExecutionExplainEntity.ExecutionPlan; +import com.arangodb.entity.*; +import com.arangodb.entity.AqlParseEntity.AstNode; +import com.arangodb.entity.QueryCachePropertiesEntity.CacheMode; +import com.arangodb.model.*; +import com.arangodb.util.RawJson; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Mark Vollmary + * @author Michele Rastelli + */ +class ArangoDatabaseTest extends BaseTest { + + private static final String COLLECTION_NAME = "db_test"; + private static final String GRAPH_NAME = "graph_test"; + + @Test + void create() throws InterruptedException, ExecutionException { + try { + final Boolean result = arangoDB.db(DbName.of(BaseTest.TEST_DB.get() + "_1")).create().get(); + assertThat(result).isTrue(); + } finally { + arangoDB.db(DbName.of(BaseTest.TEST_DB.get() + "_1")).drop().get(); + } + } + + @Test + void getVersion() throws InterruptedException, ExecutionException { + db.getVersion() + .whenComplete((version, ex) -> { + assertThat(version).isNotNull(); + assertThat(version.getServer()).isNotNull(); + assertThat(version.getVersion()).isNotNull(); + }) + .get(); + } + + @Test + void getEngine() throws ExecutionException, InterruptedException { + final ArangoDBEngine engine = db.getEngine().get(); + assertThat(engine).isNotNull(); + assertThat(engine.getName()).isNotNull(); + } + + @Test + void exists() throws InterruptedException, ExecutionException { + assertThat(db.exists().get()).isTrue(); + assertThat(arangoDB.db(DbName.of("no")).exists().get()).isFalse(); + } + + @Test + void getAccessibleDatabases() throws InterruptedException, ExecutionException { + db.getAccessibleDatabases() + .whenComplete((dbs, ex) -> { + assertThat(dbs).isNotNull(); + assertThat(dbs.size()).isGreaterThan(0); + assertThat(dbs).contains("_system"); + }) + .get(); + } + + @Test + void createCollection() throws InterruptedException, ExecutionException { + db.createCollection(COLLECTION_NAME, null) + .whenComplete((result, ex) -> { + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + }) + .get(); + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void createCollectionWithReplicationFactor() throws InterruptedException, ExecutionException { + assumeTrue(isCluster()); + final CollectionEntity result = db + .createCollection(COLLECTION_NAME, new CollectionCreateOptions().replicationFactor(2)).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + assertThat(db.collection(COLLECTION_NAME).getProperties().get().getReplicationFactor().getValue()).isEqualTo(2); + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void createCollectionWithWriteConcern() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isCluster()); + + final CollectionEntity result = db.createCollection(COLLECTION_NAME, + new CollectionCreateOptions().replicationFactor(2).writeConcern(2)).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + assertThat(db.collection(COLLECTION_NAME).getProperties().get().getReplicationFactor().getValue()).isEqualTo(2); + assertThat(db.collection(COLLECTION_NAME).getProperties().get().getWriteConcern()).isEqualTo(2); + db.collection(COLLECTION_NAME).drop(); + } + + @Test + void createCollectionWithNumberOfShards() throws InterruptedException, ExecutionException { + assumeTrue(isCluster()); + final CollectionEntity result = db + .createCollection(COLLECTION_NAME, new CollectionCreateOptions().numberOfShards(2)).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + assertThat(db.collection(COLLECTION_NAME).getProperties().get().getNumberOfShards()).isEqualTo(2); + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void createCollectionWithNumberOfShardsAndShardKey() throws InterruptedException, ExecutionException { + assumeTrue(isCluster()); + final CollectionEntity result = db + .createCollection(COLLECTION_NAME, new CollectionCreateOptions().numberOfShards(2).shardKeys("a")) + .get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + final CollectionPropertiesEntity properties = db.collection(COLLECTION_NAME).getProperties().get(); + assertThat(properties.getNumberOfShards()).isEqualTo(2); + assertThat(properties.getShardKeys()).hasSize(1); + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void createCollectionWithNumberOfShardsAndShardKeys() throws InterruptedException, ExecutionException { + assumeTrue(isCluster()); + final CollectionEntity result = db.createCollection(COLLECTION_NAME, + new CollectionCreateOptions().numberOfShards(2).shardKeys("a", "b")).get(); + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + final CollectionPropertiesEntity properties = db.collection(COLLECTION_NAME).getProperties().get(); + assertThat(properties.getNumberOfShards()).isEqualTo(2); + assertThat(properties.getShardKeys()).hasSize(2); + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void deleteCollection() throws InterruptedException, ExecutionException { + db.createCollection(COLLECTION_NAME, null).get(); + db.collection(COLLECTION_NAME).drop().get(); + try { + db.collection(COLLECTION_NAME).getInfo().get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void deleteSystemCollection() throws InterruptedException, ExecutionException { + final String name = "_system_test"; + db.createCollection(name, new CollectionCreateOptions().isSystem(true)).get(); + db.collection(name).drop(true).get(); + try { + db.collection(name).getInfo().get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void deleteSystemCollectionFail() throws InterruptedException, ExecutionException { + final String name = "_system_test"; + db.createCollection(name, new CollectionCreateOptions().isSystem(true)).get(); + try { + db.collection(name).drop().get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + db.collection(name).drop(true).get(); + try { + db.collection(name).getInfo().get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void getIndex() throws InterruptedException, ExecutionException { + db.createCollection(COLLECTION_NAME, null).get(); + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = db.collection(COLLECTION_NAME).ensureHashIndex(fields, null).get(); + db.getIndex(createResult.getId()) + .whenComplete((readResult, ex) -> { + assertThat(readResult.getId()).isEqualTo(createResult.getId()); + assertThat(readResult.getType()).isEqualTo(createResult.getType()); + }) + .get(); + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void deleteIndex() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + final Collection fields = new ArrayList<>(); + fields.add("a"); + final IndexEntity createResult = db.collection(COLLECTION_NAME).ensureHashIndex(fields, null).get(); + db.deleteIndex(createResult.getId()) + .whenComplete((id, ex) -> { + assertThat(id).isEqualTo(createResult.getId()); + try { + db.getIndex(id).get(); + fail(); + } catch (InterruptedException e) { + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + }) + .get(); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void getCollections() throws InterruptedException, ExecutionException { + try { + final Collection systemCollections = db.getCollections(null).get(); + db.createCollection(COLLECTION_NAME + "1", null).get(); + db.createCollection(COLLECTION_NAME + "2", null).get(); + db.getCollections(null) + .whenComplete((collections, ex) -> { + assertThat(collections.size()).isEqualTo(2 + systemCollections.size()); + assertThat(collections).isNotNull(); + }) + .get(); + } finally { + db.collection(COLLECTION_NAME + "1").drop().get(); + db.collection(COLLECTION_NAME + "2").drop().get(); + } + } + + @Test + void getCollectionsExcludeSystem() throws InterruptedException, ExecutionException { + try { + final CollectionsReadOptions options = new CollectionsReadOptions().excludeSystem(true); + final Collection systemCollections = db.getCollections(options).get(); + assertThat(systemCollections).isEmpty(); + db.createCollection(COLLECTION_NAME + "1", null).get(); + db.createCollection(COLLECTION_NAME + "2", null).get(); + db.getCollections(options) + .whenComplete((collections, ex) -> { + assertThat(collections.size()).isEqualTo(2); + assertThat(collections).isNotNull(); + }) + .get(); + } finally { + db.collection(COLLECTION_NAME + "1").drop().get(); + db.collection(COLLECTION_NAME + "2").drop().get(); + } + } + + @Test + void grantAccess() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.grantAccess("user1").get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void grantAccessRW() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.grantAccess("user1", Permissions.RW).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void grantAccessRO() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.grantAccess("user1", Permissions.RO).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void grantAccessNONE() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.grantAccess("user1", Permissions.NONE).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void grantAccessUserNotFound() { + Throwable thrown = catchThrowable(() -> db.grantAccess("user1", Permissions.RW).get()); + assertThat(thrown).isInstanceOf(ExecutionException.class); + } + + @Test + void revokeAccess() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.revokeAccess("user1").get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void revokeAccessUserNotFound() { + Throwable thrown = catchThrowable(() -> db.revokeAccess("user1").get()); + assertThat(thrown).isInstanceOf(ExecutionException.class); + } + + @Test + void resetAccess() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234", null).get(); + db.resetAccess("user1").get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void resetAccessUserNotFound() { + Throwable thrown = catchThrowable(() -> db.resetAccess("user1").get()); + assertThat(thrown).isInstanceOf(ExecutionException.class); + } + + @Test + void grantDefaultCollectionAccess() throws InterruptedException, ExecutionException { + try { + arangoDB.createUser("user1", "1234").get(); + db.grantDefaultCollectionAccess("user1", Permissions.RW).get(); + } finally { + arangoDB.deleteUser("user1").get(); + } + } + + @Test + void getPermissions() throws InterruptedException, ExecutionException { + assertThat(db.getPermissions("root").get()).isEqualTo(Permissions.RW); + } + + @Test + void query() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + db.query("for i in db_test return i._id", null, null, String.class) + .whenComplete((cursor, ex) -> { + for (int i = 0; i < 10; i++, cursor.next()) { + assertThat(cursor.hasNext()).isEqualTo(true); + } + }) + .get(); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void queryForEach() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + db.query("for i in db_test return i._id", null, null, String.class) + .whenComplete((cursor, ex) -> { + final AtomicInteger i = new AtomicInteger(0); + cursor.forEachRemaining(e -> i.incrementAndGet()); + assertThat(i.get()).isEqualTo(10); + }) + .get(); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void queryStream() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + db.query("for i in db_test return i._id", null, null, String.class) + .whenComplete((cursor, ex) -> { + final AtomicInteger i = new AtomicInteger(0); + cursor.forEachRemaining(e -> i.incrementAndGet()); + assertThat(i.get()).isEqualTo(10); + }) + .get(); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void queryWithTimeout() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 6)); + + try { + db.query("RETURN SLEEP(1)", null, new AqlQueryOptions().maxRuntime(0.1), String.class).get().next(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) e.getCause()).getResponseCode()).isEqualTo(410); + } + } + + @Test + void queryWithCount() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + + db.query("for i in db_test Limit 6 return i._id", null, new AqlQueryOptions().count(true), String.class) + .whenComplete((cursor, ex) -> { + for (int i = 0; i < 6; i++, cursor.next()) { + assertThat(cursor.hasNext()).isEqualTo(true); + } + assertThat(cursor.getCount()).isEqualTo(6); + }) + .get(); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void queryWithLimitAndFullCount() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + + db.query("for i in db_test Limit 5 return i._id", null, new AqlQueryOptions().fullCount(true), String.class) + .whenComplete((cursor, ex) -> { + for (int i = 0; i < 5; i++, cursor.next()) { + assertThat(cursor.hasNext()).isEqualTo(true); + } + assertThat(cursor.getStats()).isNotNull(); + assertThat(((Number) cursor.getStats().get("fullCount")).intValue()) + .isGreaterThanOrEqualTo(10); + }) + .get(); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void queryWithBatchSize() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + final ArangoCursorAsync cursor = db.query("for i in db_test return i._id", null, + new AqlQueryOptions().batchSize(5).count(true), String.class).get(); + for (int i = 0; i < 10; i++, cursor.next()) { + assertThat(cursor.hasNext()).isTrue(); + } + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void queryStreamWithBatchSize() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + final ArangoCursorAsync cursor = db.query("for i in db_test return i._id", null, + new AqlQueryOptions().batchSize(5).count(true), String.class).get(); + final AtomicInteger i = new AtomicInteger(0); + cursor.streamRemaining().forEach(e -> i.incrementAndGet()); + assertThat(i.get()).isEqualTo(10); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + /** + * ignored. takes to long + */ + @Test + @Disabled + void queryWithTTL() throws InterruptedException, ExecutionException { + // set TTL to 1 seconds and get the second batch after 2 seconds! + final int ttl = 1; + final int wait = 2; + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + final ArangoCursorAsync cursor = db.query("for i in db_test return i._id", null, + new AqlQueryOptions().batchSize(5).ttl(ttl), String.class).get(); + for (int i = 0; i < 10; i++, cursor.next()) { + assertThat(cursor.hasNext()).isTrue(); + if (i == 1) { + Thread.sleep(wait * 1000); + } + } + fail(); + } catch (final ArangoDBException ex) { + assertThat(ex.getResponseCode()).isEqualTo(404); + assertThat(ex.getErrorNum()).isEqualTo(1600); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void changeQueryCache() throws InterruptedException, ExecutionException { + try { + 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); + } finally { + final QueryCachePropertiesEntity properties = new QueryCachePropertiesEntity(); + properties.setMode(CacheMode.off); + db.setQueryCacheProperties(properties).get(); + } + } + + @Test + void queryWithCache() throws InterruptedException, ExecutionException { + assumeTrue(isSingleServer()); + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + db.collection(COLLECTION_NAME).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 db_test FILTER t.age >= 10 SORT t.age RETURN t._id", null, + new AqlQueryOptions().cache(true), String.class) + .get(); + + assertThat(cursor.isCached()).isFalse(); + + final ArangoCursorAsync cachedCursor = db + .query("FOR t IN db_test FILTER t.age >= 10 SORT t.age RETURN t._id", null, + new AqlQueryOptions().cache(true), String.class) + .get(); + + assertThat(cachedCursor.isCached()).isTrue(); + + } finally { + db.collection(COLLECTION_NAME).drop().get(); + final QueryCachePropertiesEntity properties = new QueryCachePropertiesEntity(); + properties.setMode(CacheMode.off); + db.setQueryCacheProperties(properties).get(); + } + } + + @Test + void queryCursor() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + final int numbDocs = 10; + for (int i = 0; i < numbDocs; i++) { + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(), null).get(); + } + + final int batchSize = 5; + final ArangoCursorAsync cursor = db.query("for i in db_test return i._id", null, + new AqlQueryOptions().batchSize(batchSize).count(true), String.class).get(); + assertThat(cursor.getCount()).isEqualTo(numbDocs); + + final ArangoCursorAsync cursor2 = db.cursor(cursor.getId(), String.class).get(); + assertThat(cursor2.getCount()).isEqualTo(numbDocs); + assertThat(cursor2.hasNext()).isTrue(); + + for (int i = 0; i < batchSize; i++, cursor.next()) { + assertThat(cursor.hasNext()).isTrue(); + } + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void changeQueryTrackingProperties() throws InterruptedException, ExecutionException { + 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(); + } + } + + @Test + void queryWithBindVars() throws InterruptedException, ExecutionException { + try { + db.createCollection(COLLECTION_NAME, null).get(); + for (int i = 0; i < 10; i++) { + final BaseDocument baseDocument = new BaseDocument(UUID.randomUUID().toString()); + baseDocument.addAttribute("age", 20 + i); + db.collection(COLLECTION_NAME).insertDocument(baseDocument, null).get(); + } + final Map bindVars = new HashMap<>(); + bindVars.put("@coll", COLLECTION_NAME); + bindVars.put("age", 25); + db.query("FOR t IN @@coll FILTER t.age >= @age SORT t.age RETURN t._id", bindVars, null, String.class) + .whenComplete((cursor, ex) -> { + for (int i = 0; i < 5; i++, cursor.next()) { + assertThat(cursor.hasNext()).isEqualTo(true); + } + }) + .get(); + } finally { + db.collection(COLLECTION_NAME).drop().get(); + } + } + + @Test + void queryWithWarning() throws InterruptedException, ExecutionException { + arangoDB.db().query("return 1/0", null, null, String.class) + .whenComplete((cursor, ex) -> { + assertThat(cursor.getWarnings()).isNotNull(); + assertThat(cursor.getWarnings()).isNotEmpty(); + }) + .get(); + } + + @Test + void queryClose() throws IOException, InterruptedException, ExecutionException { + final ArangoCursorAsync cursor = arangoDB.db() + .query("for i in 1..2 return i", null, new AqlQueryOptions().batchSize(1), String.class).get(); + cursor.close(); + int count = 0; + try { + for (; cursor.hasNext(); cursor.next(), count++) { + } + fail(); + } catch (final ArangoDBException e) { + assertThat(count).isEqualTo(1); + } + + } + + @Test + void explainQuery() throws InterruptedException, ExecutionException { + arangoDB.db().explainQuery("for i in 1..1 return i", null, null) + .whenComplete((explain, ex) -> { + assertThat(explain).isNotNull(); + assertThat(explain.getPlan()).isNotNull(); + assertThat(explain.getPlans()).isNull(); + final ExecutionPlan plan = explain.getPlan(); + assertThat(plan.getCollections().size()).isEqualTo(0); + assertThat(plan.getEstimatedCost()).isGreaterThan(0); + assertThat(plan.getEstimatedNrItems()).isGreaterThan(0); + assertThat(plan.getVariables().size()).isEqualTo(2); + assertThat(plan.getNodes().size()).isGreaterThan(0); + }) + .get(); + } + + @Test + void parseQuery() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + arangoDB.db().parseQuery("for i in _apps return i") + .whenComplete((parse, ex) -> { + assertThat(parse).isNotNull(); + assertThat(parse.getBindVars()).isEmpty(); + assertThat(parse.getCollections().size()).isEqualTo(1); + assertThat(parse.getCollections().iterator().next()).isEqualTo("_apps"); + assertThat(parse.getAst().size()).isEqualTo(1); + final AstNode root = parse.getAst().iterator().next(); + assertThat(root.getType()).isEqualTo("root"); + assertThat(root.getName()).isNull(); + assertThat(root.getSubNodes()).isNotNull(); + assertThat(root.getSubNodes().size()).isEqualTo(2); + final Iterator iterator = root.getSubNodes().iterator(); + final AstNode for_ = iterator.next(); + assertThat(for_.getType()).isEqualTo("for"); + assertThat(for_.getSubNodes()).isNotNull(); + assertThat(for_.getSubNodes().size()).isEqualTo(3); + final Iterator iterator2 = for_.getSubNodes().iterator(); + final AstNode first = iterator2.next(); + assertThat(first.getType()).isEqualTo("variable"); + assertThat(first.getName()).isEqualTo("i"); + final AstNode second = iterator2.next(); + assertThat(second.getType()).isEqualTo("collection"); + assertThat(second.getName()).isEqualTo("_apps"); + final AstNode return_ = iterator.next(); + assertThat(return_.getType()).isEqualTo("return"); + assertThat(return_.getSubNodes()).isNotNull(); + assertThat(return_.getSubNodes().size()).isEqualTo(1); + assertThat(return_.getSubNodes().iterator().next().getType()).isEqualTo("reference"); + assertThat(return_.getSubNodes().iterator().next().getName()).isEqualTo("i"); + }) + .get(); + } + + @Test + @Disabled + void getAndClearSlowQueries() throws InterruptedException, ExecutionException { + final QueryTrackingPropertiesEntity properties = db.getQueryTrackingProperties().get(); + final Long slowQueryThreshold = properties.getSlowQueryThreshold(); + try { + properties.setSlowQueryThreshold(1L); + db.setQueryTrackingProperties(properties); + + db.query("return sleep(1.1)", null, null, Void.class); + final Collection slowQueries = db.getSlowQueries().get(); + assertThat(slowQueries).isNotNull(); + assertThat(slowQueries).hasSize(1); + final QueryEntity queryEntity = slowQueries.iterator().next(); + assertThat(queryEntity.getQuery()).isEqualTo("return sleep(1.1)"); + + db.clearSlowQueries().get(); + assertThat(db.getSlowQueries().get().size()).isZero(); + } finally { + properties.setSlowQueryThreshold(slowQueryThreshold); + db.setQueryTrackingProperties(properties); + } + } + + @Test + void createGetDeleteAqlFunction() throws InterruptedException, ExecutionException { + 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).hasSameSizeAs(aqlFunctionsInitial); + } + } + + @Test + void createGetDeleteAqlFunctionWithNamespace() throws InterruptedException, ExecutionException { + 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 { + db.deleteAqlFunction("myfunctions::temperature", new AqlFunctionDeleteOptions().group(true)).get(); + + final Collection aqlFunctions = db.getAqlFunctions(null).get(); + assertThat(aqlFunctions).hasSameSizeAs(aqlFunctionsInitial); + } + } + + @Test + void createGraph() throws InterruptedException, ExecutionException { + try { + db.createGraph(GRAPH_NAME, null, null) + .whenComplete((result, ex) -> { + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo(GRAPH_NAME); + }) + .get(); + } finally { + db.graph(GRAPH_NAME).drop().get(); + } + } + + @Test + void getGraphs() throws InterruptedException, ExecutionException { + try { + db.createGraph(GRAPH_NAME, null, null).get(); + db.getGraphs() + .whenComplete((graphs, ex) -> { + assertThat(graphs).isNotNull(); + assertThat(graphs.size()).isEqualTo(1); + }) + .get(); + } finally { + db.graph(GRAPH_NAME).drop().get(); + } + } + + @Test + void transactionString() throws InterruptedException, ExecutionException { + final TransactionOptions options = new TransactionOptions().params("test"); + db.transaction("function (params) {return params;}", RawJson.class, options) + .whenComplete((result, ex) -> assertThat(result.getValue()).isEqualTo("\"test\"")) + .get(); + } + + @Test + void transactionNumber() throws InterruptedException, ExecutionException { + final TransactionOptions options = new TransactionOptions().params(5); + db.transaction("function (params) {return params;}", Integer.class, options) + .whenComplete((result, ex) -> assertThat(result).isEqualTo(5)) + .get(); + } + + @Test + void transactionJsonNode() throws InterruptedException, ExecutionException { + final TransactionOptions options = new TransactionOptions().params(JsonNodeFactory.instance.textNode("test")); + db.transaction("function (params) {return params;}", JsonNode.class, options) + .whenComplete((result, ex) -> { + assertThat(result.isTextual()).isEqualTo(true); + assertThat(result.asText()).isEqualTo("test"); + }) + .get(); + } + + @Test + void transactionEmpty() throws InterruptedException, ExecutionException { + db.transaction("function () {}", Void.class, null).get(); + } + + @Test + void transactionAllowImplicit() throws InterruptedException, ExecutionException { + try { + db.createCollection("someCollection", null).get(); + db.createCollection("someOtherCollection", null).get(); + final String action = "function (params) {" + "var db = require('internal').db;" + + "return {'a':db.someCollection.all().toArray()[0], 'b':db.someOtherCollection.all().toArray()" + + "[0]};" + + "}"; + final TransactionOptions options = new TransactionOptions().readCollections("someCollection"); + db.transaction(action, Void.class, options).get(); + try { + options.allowImplicit(false); + db.transaction(action, Void.class, options).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } finally { + db.collection("someCollection").drop().get(); + db.collection("someOtherCollection").drop().get(); + } + } + + @Test + void transactionPojoReturn() throws ExecutionException, InterruptedException { + final String action = "function() { return {'value':'hello world'}; }"; + db.transaction(action, TransactionTestEntity.class, new TransactionOptions()) + .whenComplete((res, ex) -> { + assertThat(res).isNotNull(); + assertThat(res.value).isEqualTo("hello world"); + }) + .get(); + } + + @Test + void getInfo() throws InterruptedException, ExecutionException { + final CompletableFuture f = db.getInfo(); + assertThat(f).isNotNull(); + f.whenComplete((info, ex) -> { + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(TEST_DB.get()); + assertThat(info.getPath()).isNotNull(); + assertThat(info.getIsSystem()).isFalse(); + + try { + if (isAtLeastVersion(3, 6) && isCluster()) { + assertThat(info.getSharding()).isNotNull(); + assertThat(info.getWriteConcern()).isNotNull(); + assertThat(info.getReplicationFactor()).isNotNull(); + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + fail(); + } + + }); + f.get(); + } + + @Test + void getDocument() throws InterruptedException, ExecutionException { + String collectionName = COLLECTION_NAME + "getDocument"; + db.createCollection(collectionName).get(); + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.setKey("123"); + db.collection(collectionName).insertDocument(value).get(); + db.getDocument(collectionName + "/123", BaseDocument.class) + .whenComplete((document, ex) -> { + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo("123"); + }) + .get(); + db.collection(collectionName).drop().get(); + } + + @Test + void shouldIncludeExceptionMessage() throws InterruptedException, ExecutionException { + final String exceptionMessage = "My error context"; + final String action = "function (params) {" + "throw '" + exceptionMessage + "';" + "}"; + try { + db.transaction(action, Void.class, null).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + ArangoDBException cause = ((ArangoDBException) e.getCause()); + assertThat(cause.getErrorNum()).isEqualTo(1650); + if (isAtLeastVersion(3, 4)) { + assertThat(cause.getErrorMessage()).isEqualTo(exceptionMessage); + } + } + } + + @Test + void getDocumentWrongId() { + Throwable thrown = catchThrowable(() -> db.getDocument("123", BaseDocument.class).get()); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + } + + @Test + void reloadRouting() throws InterruptedException, ExecutionException { + db.reloadRouting().get(); + } + + public static class TransactionTestEntity { + private String value; + + public TransactionTestEntity() { + super(); + } + + public String getValue() { + return value; + } + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoEdgeCollectionTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoEdgeCollectionTest.java new file mode 100644 index 000000000..e1b106875 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoEdgeCollectionTest.java @@ -0,0 +1,367 @@ +/* + * 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.async; + +import com.arangodb.ArangoDBException; +import com.arangodb.entity.*; +import com.arangodb.model.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author Mark Vollmary + */ +class ArangoEdgeCollectionTest extends BaseTest { + + private static final String GRAPH_NAME = "db_collection_test"; + private static final String EDGE_COLLECTION_NAME = "db_edge_collection_test"; + private static final String VERTEX_COLLECTION_NAME = "db_vertex_collection_test"; + + @BeforeAll + static void setup() throws InterruptedException, ExecutionException { + if (!db.collection(VERTEX_COLLECTION_NAME).exists().get()) { + db.createCollection(VERTEX_COLLECTION_NAME, null).get(); + } + if (!db.collection(EDGE_COLLECTION_NAME).exists().get()) { + db.createCollection(EDGE_COLLECTION_NAME, new CollectionCreateOptions().type(CollectionType.EDGES)).get(); + } + final Collection edgeDefinitions = new ArrayList<>(); + edgeDefinitions.add(new EdgeDefinition().collection(EDGE_COLLECTION_NAME).from(VERTEX_COLLECTION_NAME) + .to(VERTEX_COLLECTION_NAME)); + db.createGraph(GRAPH_NAME, edgeDefinitions, null).get(); + } + + @AfterEach + void teardown() throws InterruptedException, ExecutionException { + for (final String collection : new String[]{VERTEX_COLLECTION_NAME, EDGE_COLLECTION_NAME}) { + db.collection(collection).truncate().get(); + } + } + + private BaseEdgeDocument createEdgeValue() throws InterruptedException, ExecutionException { + final VertexEntity v1 = db.graph(GRAPH_NAME).vertexCollection(VERTEX_COLLECTION_NAME) + .insertVertex(new BaseDocument(), null).get(); + final VertexEntity v2 = db.graph(GRAPH_NAME).vertexCollection(VERTEX_COLLECTION_NAME) + .insertVertex(new BaseDocument(), null).get(); + + final BaseEdgeDocument value = new BaseEdgeDocument(); + value.setFrom(v1.getId()); + value.setTo(v2.getId()); + return value; + } + + @Test + void insertEdge() throws InterruptedException, ExecutionException { + final BaseEdgeDocument value = createEdgeValue(); + final EdgeEntity edge = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(value, null).get(); + assertThat(edge).isNotNull(); + final BaseEdgeDocument document = db.collection(EDGE_COLLECTION_NAME) + .getDocument(edge.getKey(), BaseEdgeDocument.class, null).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + } + + @Test + void getEdge() throws InterruptedException, ExecutionException { + final BaseEdgeDocument value = createEdgeValue(); + final EdgeEntity edge = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(value, null).get(); + final BaseDocument document = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(edge.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + } + + @Test + void getEdgeIfMatch() throws InterruptedException, ExecutionException { + final BaseEdgeDocument value = createEdgeValue(); + final EdgeEntity edge = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(value, null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch(edge.getRev()); + final BaseDocument document = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(edge.getKey(), BaseDocument.class, options).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + } + + @Test + void getEdgeIfMatchFail() throws InterruptedException, ExecutionException { + final BaseEdgeDocument value = createEdgeValue(); + final EdgeEntity edge = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(value, null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch("no"); + BaseEdgeDocument doc = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(edge.getKey(), BaseEdgeDocument.class, options).get(); + assertThat(doc).isNull(); + } + + @Test + void getEdgeIfNoneMatch() throws InterruptedException, ExecutionException { + final BaseEdgeDocument value = createEdgeValue(); + final EdgeEntity edge = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(value, null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch("no"); + final BaseDocument document = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(edge.getKey(), BaseDocument.class, options).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(edge.getKey()); + } + + @Test + void getEdgeIfNoneMatchFail() throws InterruptedException, ExecutionException { + final BaseEdgeDocument value = createEdgeValue(); + final EdgeEntity edge = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(value, null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch(edge.getRev()); + BaseEdgeDocument doc = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(edge.getKey(), BaseEdgeDocument.class, options).get(); + assertThat(doc).isNull(); + } + + @Test + void replaceEdge() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final EdgeUpdateEntity replaceResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .replaceEdge(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 BaseEdgeDocument readResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(createResult.getKey(), BaseEdgeDocument.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"); + } + + @Test + void replaceEdgeIfMatch() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final EdgeReplaceOptions options = new EdgeReplaceOptions().ifMatch(createResult.getRev()); + final EdgeUpdateEntity replaceResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(createResult.getKey(), BaseEdgeDocument.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"); + } + + @Test + void replaceEdgeIfMatchFail() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + try { + final EdgeReplaceOptions options = new EdgeReplaceOptions().ifMatch("no"); + db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).replaceEdge(createResult.getKey(), doc, options) + .get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void updateEdge() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final EdgeUpdateEntity updateResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .updateEdge(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 BaseEdgeDocument readResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(createResult.getKey(), BaseEdgeDocument.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"); + } + + @Test + void updateEdgeIfMatch() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().ifMatch(createResult.getRev()); + final EdgeUpdateEntity updateResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(createResult.getKey(), BaseEdgeDocument.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"); + } + + @Test + void updateEdgeIfMatchFail() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + try { + final EdgeUpdateOptions options = new EdgeUpdateOptions().ifMatch("no"); + db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).updateEdge(createResult.getKey(), doc, options) + .get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void updateEdgeKeepNullTrue() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + doc.updateAttribute("a", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().keepNull(true); + final EdgeUpdateEntity updateResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(createResult.getKey(), BaseEdgeDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties().keySet()).hasSize(6); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @Test + void updateEdgeKeepNullFalse() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + doc.addAttribute("a", "test"); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + doc.updateAttribute("a", null); + final EdgeUpdateOptions options = new EdgeUpdateOptions().keepNull(false); + final EdgeUpdateEntity updateResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(createResult.getKey(), BaseEdgeDocument.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"); + } + + @Test + void deleteEdge() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).deleteEdge(createResult.getKey(), null).get(); + BaseEdgeDocument res = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(createResult.getKey(), BaseEdgeDocument.class, new GraphDocumentReadOptions()).get(); + assertThat(res).isNull(); + } + + @Test + void deleteEdgeIfMatch() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + final EdgeDeleteOptions options = new EdgeDeleteOptions().ifMatch(createResult.getRev()); + db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).deleteEdge(createResult.getKey(), options).get(); + BaseEdgeDocument res = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME) + .getEdge(createResult.getKey(), BaseEdgeDocument.class, new GraphDocumentReadOptions()).get(); + assertThat(res).isNull(); + } + + @Test + void deleteEdgeIfMatchFail() throws InterruptedException, ExecutionException { + final BaseEdgeDocument doc = createEdgeValue(); + final EdgeEntity createResult = db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(doc, null) + .get(); + final EdgeDeleteOptions options = new EdgeDeleteOptions().ifMatch("no"); + try { + db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).deleteEdge(createResult.getKey(), options).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoGraphTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoGraphTest.java new file mode 100644 index 000000000..beed0bf10 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoGraphTest.java @@ -0,0 +1,387 @@ +/* + * 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.async; + +import com.arangodb.entity.*; +import com.arangodb.model.GraphCreateOptions; +import com.arangodb.model.VertexCollectionCreateOptions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Mark Vollmary + */ +class ArangoGraphTest extends BaseTest { + + private static final String GRAPH_NAME = "db_collection_test"; + private static final String EDGE_COL_1 = "db_edge1_collection_test"; + private static final String EDGE_COL_2 = "db_edge2_collection_test"; + private static final String EDGE_COL_3 = "db_edge3_collection_test"; + private static final String VERTEX_COL_1 = "db_vertex1_collection_test"; + private static final String VERTEX_COL_2 = "db_vertex2_collection_test"; + private static final String VERTEX_COL_3 = "db_vertex3_collection_test"; + private static final String VERTEX_COL_4 = "db_vertex4_collection_test"; + private static final Integer REPLICATION_FACTOR = 2; + private static final Integer NUMBER_OF_SHARDS = 2; + + @BeforeAll + static void setup() throws InterruptedException, ExecutionException { + if (db.graph(GRAPH_NAME).exists().get()) { + db.graph(GRAPH_NAME).drop().get(); + } + final Collection edgeDefinitions = new ArrayList<>(); + edgeDefinitions.add(new EdgeDefinition().collection(EDGE_COL_1).from(VERTEX_COL_1).to(VERTEX_COL_2)); + edgeDefinitions + .add(new EdgeDefinition().collection(EDGE_COL_2).from(VERTEX_COL_2).to(VERTEX_COL_1, VERTEX_COL_3)); + final GraphCreateOptions options = new GraphCreateOptions(); + if (arangoDB.getRole().get() != ServerRole.SINGLE) { + options.replicationFactor(REPLICATION_FACTOR).numberOfShards(NUMBER_OF_SHARDS); + } + db.createGraph(GRAPH_NAME, edgeDefinitions, options).get(); + } + + @AfterEach + void teardown() throws InterruptedException, ExecutionException { + for (final String collection : new String[]{EDGE_COL_1, EDGE_COL_2, VERTEX_COL_1, VERTEX_COL_2, VERTEX_COL_3, + VERTEX_COL_4}) { + final ArangoCollectionAsync c = db.collection(collection); + if (c.exists().get()) { + c.truncate().get(); + } + } + } + + @Test + void create() throws InterruptedException, ExecutionException { + try { + final GraphEntity result = db.graph(GRAPH_NAME + "_1").create(null).get(); + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo(GRAPH_NAME + "_1"); + } finally { + db.graph(GRAPH_NAME + "_1").drop().get(); + } + } + + @Test + void createWithReplicationAndWriteConcern() 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().getValue()).isEqualTo(2); + db.graph(GRAPH_NAME + "_1").drop(); + } + + @Test + void getGraphs() throws InterruptedException, ExecutionException { + final Collection graphs = db.getGraphs().get(); + assertThat(graphs).isNotNull(); + assertThat(graphs).isNotEmpty(); + } + + @Test + void getInfo() throws InterruptedException, ExecutionException { + final GraphEntity info = db.graph(GRAPH_NAME).getInfo().get(); + assertThat(info).isNotNull(); + assertThat(info.getName()).isEqualTo(GRAPH_NAME); + assertThat(info.getEdgeDefinitions()).hasSize(2); + final Iterator iterator = info.getEdgeDefinitions().iterator(); + final EdgeDefinition e1 = iterator.next(); + assertThat(e1.getCollection()).isEqualTo(EDGE_COL_1); + assertThat(e1.getFrom()).contains(VERTEX_COL_1); + assertThat(e1.getTo()).contains(VERTEX_COL_2); + final EdgeDefinition e2 = iterator.next(); + 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[]{VERTEX_COL_1, VERTEX_COL_2}) { + final CollectionPropertiesEntity properties = db.collection(collection).getProperties().get(); + assertThat(properties.getReplicationFactor().getValue()).isEqualTo(REPLICATION_FACTOR); + assertThat(properties.getNumberOfShards()).isEqualTo(NUMBER_OF_SHARDS); + } + for (final String collection : new String[]{EDGE_COL_1, EDGE_COL_2}) { + final CollectionPropertiesEntity properties = db.collection(collection).getProperties().get(); + assertThat(properties.getReplicationFactor().getValue()).isEqualTo(REPLICATION_FACTOR); + } + } + } + + @Test + void getVertexCollections() throws InterruptedException, ExecutionException { + final Collection vertexCollections = db.graph(GRAPH_NAME).getVertexCollections().get(); + assertThat(vertexCollections).isNotNull(); + assertThat(vertexCollections).hasSize(3); + assertThat(vertexCollections).contains(VERTEX_COL_1, VERTEX_COL_2, VERTEX_COL_3); + } + + @Test + void addVertexCollection() throws InterruptedException, ExecutionException { + final GraphEntity graph = db.graph(GRAPH_NAME).addVertexCollection(VERTEX_COL_4).get(); + assertThat(graph).isNotNull(); + final Collection vertexCollections = db.graph(GRAPH_NAME).getVertexCollections().get(); + assertThat(vertexCollections).contains(VERTEX_COL_1, VERTEX_COL_2, VERTEX_COL_3, VERTEX_COL_4); + setup(); + } + + @Test + void addSatelliteVertexCollection() throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + assumeTrue(isEnterprise()); + assumeTrue(isAtLeastVersion(3, 9)); + + String v1Name = "vertex-" + rnd(); + + ArangoGraphAsync g = db.graph(GRAPH_NAME + rnd()); + g.createGraph(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); + assertThat(db.collection(v1Name).getProperties().get().getReplicationFactor()).isEqualTo(ReplicationFactor.ofSatellite()); + + // revert + g.drop().get(); + } + + @Test + void getEdgeCollections() throws InterruptedException, ExecutionException { + final Collection edgeCollections = db.graph(GRAPH_NAME).getEdgeDefinitions().get(); + assertThat(edgeCollections).isNotNull(); + assertThat(edgeCollections).hasSize(2); + assertThat(edgeCollections).contains(EDGE_COL_1, EDGE_COL_2); + } + + @Test + void addEdgeDefinition() throws InterruptedException, ExecutionException { + final GraphEntity graph = db.graph(GRAPH_NAME) + .addEdgeDefinition(new EdgeDefinition().collection(EDGE_COL_3).from(VERTEX_COL_1).to(VERTEX_COL_2)) + .get(); + assertThat(graph).isNotNull(); + final Collection edgeDefinitions = graph.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 = db.collection(EDGE_COL_3).getProperties().get(); + assertThat(properties.getReplicationFactor().getValue()).isEqualTo(REPLICATION_FACTOR); + assertThat(properties.getNumberOfShards()).isEqualTo(NUMBER_OF_SHARDS); + } + setup(); + } + + @Test + void addSatelliteEdgeDefinition() throws ExecutionException, InterruptedException { + assumeTrue(isCluster()); + 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.createGraph(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(); + 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(); + } + + @Test + void replaceEdgeDefinition() throws InterruptedException, ExecutionException { + final GraphEntity graph = db.graph(GRAPH_NAME) + .replaceEdgeDefinition(new EdgeDefinition().collection(EDGE_COL_1).from(VERTEX_COL_3).to(VERTEX_COL_4)) + .get(); + final Collection edgeDefinitions = graph.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); + } + } + setup(); + } + + @Test + void removeEdgeDefinition() throws InterruptedException, ExecutionException { + final GraphEntity graph = db.graph(GRAPH_NAME).removeEdgeDefinition(EDGE_COL_1).get(); + final Collection edgeDefinitions = graph.getEdgeDefinitions(); + assertThat(edgeDefinitions).hasSize(1); + assertThat(edgeDefinitions.iterator().next().getCollection()).isEqualTo(EDGE_COL_2); + setup(); + } + + @Test + void smartGraph() throws InterruptedException, ExecutionException { + assumeTrue(isCluster()); + assumeTrue(isEnterprise()); + for (final String collection : new String[]{EDGE_COL_1, EDGE_COL_2, VERTEX_COL_1, VERTEX_COL_2, + VERTEX_COL_3, VERTEX_COL_4}) { + if (db.collection(collection).exists().get()) { + db.collection(collection).drop().get(); + } + } + final Collection edgeDefinitions = new ArrayList<>(); + edgeDefinitions.add(new EdgeDefinition().collection(EDGE_COL_1).from(VERTEX_COL_1).to(VERTEX_COL_2)); + edgeDefinitions + .add(new EdgeDefinition().collection(EDGE_COL_2).from(VERTEX_COL_2).to(VERTEX_COL_1, VERTEX_COL_3)); + final GraphEntity graph = db.createGraph(GRAPH_NAME + "_smart", edgeDefinitions, + new GraphCreateOptions().isSmart(true).smartGraphAttribute("test").replicationFactor(REPLICATION_FACTOR) + .numberOfShards(NUMBER_OF_SHARDS)) + .get(); + assertThat(graph).isNotNull(); + assertThat(graph.getIsSmart()).isTrue(); + assertThat(graph.getSmartGraphAttribute()).isEqualTo("test"); + assertThat(graph.getNumberOfShards()).isEqualTo(2); + if (db.graph(GRAPH_NAME + "_smart").exists().get()) { + db.graph(GRAPH_NAME + "_smart").drop().get(); + } + } + + @Test + void hybridSmartGraph() throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + 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().getValue()).isEqualTo(2); + } + + @Test + void hybridDisjointSmartGraph() throws ExecutionException, InterruptedException { + assumeTrue(isEnterprise()); + assumeTrue(isCluster()); + 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().getValue()).isEqualTo(2); + } + + @Test + void drop() throws InterruptedException, ExecutionException { + final String edgeCollection = "edge_drop"; + final String vertexCollection = "vertex_drop"; + final String graph = GRAPH_NAME + "_drop"; + final GraphEntity result = db.graph(graph).create(Collections + .singleton(new EdgeDefinition().collection(edgeCollection).from(vertexCollection).to(vertexCollection))) + .get(); + assertThat(result).isNotNull(); + db.graph(graph).drop().get(); + assertThat(db.collection(edgeCollection).exists().get()).isTrue(); + assertThat(db.collection(vertexCollection).exists().get()).isTrue(); + } + + @Test + void dropPlusDropCollections() throws InterruptedException, ExecutionException { + final String edgeCollection = "edge_dropC"; + final String vertexCollection = "vertex_dropC"; + final String graph = GRAPH_NAME + "_dropC"; + final GraphEntity result = db.graph(graph).create(Collections + .singleton(new EdgeDefinition().collection(edgeCollection).from(vertexCollection).to(vertexCollection))) + .get(); + assertThat(result).isNotNull(); + db.graph(graph).drop(true).get(); + assertThat(db.collection(edgeCollection).exists().get()).isFalse(); + assertThat(db.collection(vertexCollection).exists().get()).isFalse(); + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoRouteTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoRouteTest.java new file mode 100644 index 000000000..772e593f5 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoRouteTest.java @@ -0,0 +1,81 @@ +/* + * 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.async; + +import com.arangodb.ArangoDBException; +import com.arangodb.entity.BaseDocument; +import com.arangodb.internal.ArangoRequestParam; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author Mark Vollmary + */ +class ArangoRouteTest extends BaseTest { + + /* + @Test + void get() throws InterruptedException, ExecutionException { + final Response res = db.route("/_api/version").get().get(); + assertThat(res.getBody().get("version").isString()).isEqualTo(true); + }*/ + + /* + @Test + void withHeader() throws InterruptedException, ExecutionException { + final ArangoCollectionAsync collection = db.collection("route-test-col"); + try { + collection.create(); + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + collection.insertDocument(doc).get(); + db.route("/_api/document", doc.getId()).withHeader(ArangoRequestParam.IF_NONE_MATCH, doc.getRevision()) + .get().get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause() instanceof ArangoDBException).isEqualTo(true); + } finally { + collection.drop(); + } + } + */ + + @Test + void withParentHeader() throws InterruptedException, ExecutionException { + final ArangoCollectionAsync collection = db.collection("route-test-col"); + try { + collection.create().get(); + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + collection.insertDocument(doc).get(); + db.route("/_api/document").withHeader(ArangoRequestParam.IF_NONE_MATCH, doc.getRevision()) + .route(doc.getId()).get().get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + collection.drop().get(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoSearchTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoSearchTest.java new file mode 100644 index 000000000..84dc1ba01 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoSearchTest.java @@ -0,0 +1,551 @@ +/* + * 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.async; + +import com.arangodb.ArangoDBException; +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.InvertedIndexOptions; +import com.arangodb.model.arangosearch.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Mark Vollmary + */ + +class ArangoSearchTest extends BaseTest { + + private static final String VIEW_NAME = "view_test"; + private static final String COLL_1 = "view_update_prop_test_collection"; + private static final String COLL_2 = "view_replace_prop_test_collection"; + + @BeforeAll + static void setup() throws InterruptedException, ExecutionException { + if (!isAtLeastVersion(arangoDB, 3, 4)) + return; + db.createArangoSearch(VIEW_NAME, new ArangoSearchCreateOptions()).get(); + db.createCollection(COLL_1).get(); + db.createCollection(COLL_2).get(); + } + + @Test + void exists() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + assertThat(db.arangoSearch(VIEW_NAME).exists().get()).isTrue(); + } + + @Test + void createAndExistsSearchAlias() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = "view-" + rnd(); + db.createSearchAlias(viewName, new SearchAliasCreateOptions()).get(); + assertThat(db.arangoSearch(viewName).exists().get()).isTrue(); + } + + @Test + void getInfo() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final ViewEntity info = db.arangoSearch(VIEW_NAME).getInfo().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(VIEW_NAME); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + } + + @Test + void drop() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_droptest"; + db.createArangoSearch(name, new ArangoSearchCreateOptions()).get(); + final ArangoViewAsync view = db.arangoSearch(name); + view.drop().get(); + assertThat(view.exists().get()).isFalse(); + } + + @Test + void rename() throws InterruptedException, ExecutionException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_renametest"; + final String newName = name + "_new"; + db.createArangoSearch(name, new ArangoSearchCreateOptions()).get(); + db.arangoSearch(name).rename(newName).get(); + assertThat(db.arangoSearch(name).exists().get()).isFalse(); + assertThat(db.arangoSearch(newName).exists().get()).isTrue(); + } + + @Test + void createArangoSearchView() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_createtest"; + final ViewEntity info = db.arangoSearch(name).create().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(name); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(name).exists().get()).isTrue(); + } + + @Test + void createSearchAliasView() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = "view-" + rnd(); + 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(); + } + + @Test + void createArangoSearchViewWithOptions() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_createtest_withotpions"; + final ViewEntity info = db.arangoSearch(name).create(new ArangoSearchCreateOptions()).get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(name); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(name).exists().get()).isTrue(); + } + + @Test + void createArangoSearchViewWithPrimarySort() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final String name = "createWithPrimarySort"; + final ArangoSearchCreateOptions options = new ArangoSearchCreateOptions(); + + final PrimarySort primarySort = PrimarySort.on("myFieldName"); + primarySort.ascending(true); + options.primarySort(primarySort); + options.consolidationIntervalMsec(666666L); + + final ViewEntity info = db.arangoSearch(name).create(options).get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(name); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(name).exists().get()).isTrue(); + } + + @Test + void createArangoSearchViewWithCommitIntervalMsec() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 5)); + final String name = "createWithCommitIntervalMsec"; + final ArangoSearchCreateOptions options = new ArangoSearchCreateOptions(); + options.commitIntervalMsec(666666L); + + final ViewEntity info = db.arangoSearch(name).create(options).get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(name); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + assertThat(db.arangoSearch(name).exists().get()).isTrue(); + + // check commit interval msec property + final ArangoSearchAsync view = db.arangoSearch(name); + final ArangoSearchPropertiesEntity properties = view.getProperties().get(); + assertThat(properties.getCommitIntervalMsec()).isEqualTo(666666L); + } + + @Test + void createSearchAliasViewWithOptions() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + String viewName = "view-" + rnd(); + 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(); + } + + @Test + void createSearchAliasViewWithIndexesAndGetProperties() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollectionAsync col = db.collection(COLL_1); + String idxName = "idx-" + rnd(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName) + .fields(new InvertedIndexField().name("a" + rnd()))) + .get(); + String viewName = "view-" + rnd(); + final SearchAliasCreateOptions options = new SearchAliasCreateOptions() + .indexes(new SearchAliasIndex(COLL_1, idxName)); + 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(idxName)); + } + + @Test + void getArangoSearchViewProperties() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_getpropertiestest"; + final ArangoSearchAsync view = db.arangoSearch(name); + view.create(new ArangoSearchCreateOptions()).get(); + final ArangoSearchPropertiesEntity properties = view.getProperties().get(); + assertThat(properties).isNotNull(); + assertThat(properties.getId()).isNotNull(); + assertThat(properties.getName()).isEqualTo(name); + 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(); + } + + @Test + void updateArangoSearchViewProperties() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_updatepropertiestest"; + final ArangoSearchAsync view = db.arangoSearch(name); + 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("view_update_prop_test_collection").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("view_update_prop_test_collection"); + 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); + } + + @Test + void updateSearchAliasViewWithIndexesAndGetProperties() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollectionAsync col = db.collection(COLL_1); + String idxName = "idx-" + rnd(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName) + .fields(new InvertedIndexField().name("a" + rnd()))) + .get(); + ArangoCollectionAsync col2 = db.collection(COLL_2); + String idxName2 = "idx-" + rnd(); + col2.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName2) + .fields(new InvertedIndexField().name("a" + rnd()))) + .get(); + + String viewName = "view-" + rnd(); + 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)); + } + + @Test + void replaceArangoSearchViewProperties() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_replacepropertiestest"; + final ArangoSearchAsync view = db.arangoSearch(name); + view.create(new ArangoSearchCreateOptions()).get(); + final ArangoSearchPropertiesOptions options = new ArangoSearchPropertiesOptions(); + options.link( + CollectionLink.on("view_replace_prop_test_collection").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("view_replace_prop_test_collection"); + assertThat(link.getFields()).hasSize(1); + assertThat(link.getFields().iterator().next().getName()).isEqualTo("value"); + } + + @Test + void replaceSearchAliasViewWithIndexesAndGetProperties() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + ArangoCollectionAsync col = db.collection(COLL_1); + String idxName = "idx-" + rnd(); + col.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName) + .fields(new InvertedIndexField().name("a" + rnd()))) + .get(); + ArangoCollectionAsync col2 = db.collection(COLL_2); + String idxName2 = "idx-" + rnd(); + col2.ensureInvertedIndex(new InvertedIndexOptions() + .name(idxName2) + .fields(new InvertedIndexField().name("a" + rnd()))) + .get(); + + String viewName = "view-" + rnd(); + 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(SearchAnalyzer analyzer) throws ExecutionException, + InterruptedException { + + String fullyQualifiedName = db.dbName().get() + "::" + 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(); + + try { + db.getSearchAnalyzer(analyzer.getName()).get(); + fail("deleted analyzer should not be found!"); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) e.getCause()).getResponseCode()).isEqualTo(404); + assertThat(((ArangoDBException) e.getCause()).getErrorNum()).isEqualTo(1202); + } + + } + + @Test + void identityAnalyzerTyped() 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(analyzer); + } + + @Test + void delimiterAnalyzerTyped() 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(analyzer); + } + + @Test + void stemAnalyzerTyped() 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(options); + } + + @Test + void normAnalyzerTyped() 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(options); + } + + @Test + void ngramAnalyzerTyped() 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(analyzer); + } + + @Test + void textAnalyzerTyped() 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(analyzer); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoVertexCollectionTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoVertexCollectionTest.java new file mode 100644 index 000000000..4da5b6735 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoVertexCollectionTest.java @@ -0,0 +1,356 @@ +/* + * 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.async; + +import com.arangodb.ArangoDBException; +import com.arangodb.entity.BaseDocument; +import com.arangodb.entity.VertexEntity; +import com.arangodb.entity.VertexUpdateEntity; +import com.arangodb.model.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.Collection; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author Mark Vollmary + */ +class ArangoVertexCollectionTest extends BaseTest { + + private static final String GRAPH_NAME = "db_collection_test"; + private static final String COLLECTION_NAME = "db_vertex_collection_test"; + + @BeforeAll + static void setup() throws InterruptedException, ExecutionException { + if (!db.collection(COLLECTION_NAME).exists().get()) { + db.createCollection(COLLECTION_NAME, null).get(); + } + final GraphCreateOptions options = new GraphCreateOptions().orphanCollections(COLLECTION_NAME); + db.createGraph(GRAPH_NAME, null, options).get(); + } + + @AfterEach + void teardown() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).truncate().get(); + } + + @Test + void dropVertexCollection() throws InterruptedException, ExecutionException { + db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).drop().get(); + final Collection vertexCollections = db.graph(GRAPH_NAME).getVertexCollections().get(); + assertThat(vertexCollections).doesNotContain(COLLECTION_NAME); + } + + @Test + void insertVertex() throws InterruptedException, ExecutionException { + final VertexEntity vertex = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .insertVertex(new BaseDocument(), null).get(); + assertThat(vertex).isNotNull(); + final BaseDocument document = db.collection(COLLECTION_NAME) + .getDocument(vertex.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @Test + void getVertex() throws InterruptedException, ExecutionException { + final VertexEntity vertex = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .insertVertex(new BaseDocument(), null).get(); + final BaseDocument document = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .getVertex(vertex.getKey(), BaseDocument.class, null).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @Test + void getVertexIfMatch() throws InterruptedException, ExecutionException { + final VertexEntity vertex = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .insertVertex(new BaseDocument(), null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch(vertex.getRev()); + final BaseDocument document = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .getVertex(vertex.getKey(), BaseDocument.class, options).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @Test + void getVertexIfMatchFail() throws InterruptedException, ExecutionException { + final VertexEntity vertex = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .insertVertex(new BaseDocument(), null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifMatch("no"); + BaseDocument res = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .getVertex(vertex.getKey(), BaseDocument.class, options).get(); + assertThat(res).isNull(); + } + + @Test + void getVertexIfNoneMatch() throws InterruptedException, ExecutionException { + final VertexEntity vertex = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .insertVertex(new BaseDocument(), null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch("no"); + final BaseDocument document = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .getVertex(vertex.getKey(), BaseDocument.class, options).get(); + assertThat(document).isNotNull(); + assertThat(document.getKey()).isEqualTo(vertex.getKey()); + } + + @Test + void getVertexIfNoneMatchFail() throws InterruptedException, ExecutionException { + final VertexEntity vertex = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .insertVertex(new BaseDocument(), null).get(); + final GraphDocumentReadOptions options = new GraphDocumentReadOptions().ifNoneMatch(vertex.getRev()); + BaseDocument res = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .getVertex(vertex.getKey(), BaseDocument.class, options).get(); + assertThat(res).isNull(); + } + + @Test + void replaceVertex() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final VertexUpdateEntity replaceResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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"); + } + + @Test + void replaceVertexIfMatch() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + final VertexReplaceOptions options = new VertexReplaceOptions().ifMatch(createResult.getRev()); + final VertexUpdateEntity replaceResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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"); + } + + @Test + void replaceVertexIfMatchFail() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + doc.removeAttribute("a"); + doc.addAttribute("b", "test"); + try { + final VertexReplaceOptions options = new VertexReplaceOptions().ifMatch("no"); + db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).replaceVertex(createResult.getKey(), doc, options) + .get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void updateVertex() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + final VertexUpdateEntity updateResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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"); + } + + @Test + void updateVertexIfMatch() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).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 = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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"); + } + + @Test + void updateVertexIfMatchFail() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + doc.addAttribute("c", "test"); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + doc.updateAttribute("a", "test1"); + doc.addAttribute("b", "test"); + doc.updateAttribute("c", null); + try { + final VertexUpdateOptions options = new VertexUpdateOptions().ifMatch("no"); + db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).updateVertex(createResult.getKey(), doc, options) + .get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void updateVertexKeepNullTrue() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + doc.updateAttribute("a", null); + final VertexUpdateOptions options = new VertexUpdateOptions().keepNull(true); + final VertexUpdateEntity updateResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .getVertex(createResult.getKey(), BaseDocument.class, null).get(); + assertThat(readResult.getKey()).isEqualTo(createResult.getKey()); + assertThat(readResult.getProperties().keySet()).hasSize(4); + assertThat(readResult.getProperties()).containsKey("a"); + } + + @Test + void updateVertexKeepNullFalse() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + doc.addAttribute("a", "test"); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + doc.updateAttribute("a", null); + final VertexUpdateOptions options = new VertexUpdateOptions().keepNull(false); + final VertexUpdateEntity updateResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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 = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .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"); + } + + @Test + void deleteVertex() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).deleteVertex(createResult.getKey(), null).get(); + BaseDocument res = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .getVertex(createResult.getKey(), BaseDocument.class, new GraphDocumentReadOptions()).get(); + assertThat(res).isNull(); + } + + @Test + void deleteVertexIfMatch() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + final VertexDeleteOptions options = new VertexDeleteOptions().ifMatch(createResult.getRev()); + db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).deleteVertex(createResult.getKey(), options).get(); + BaseDocument res = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME) + .getVertex(createResult.getKey(), BaseDocument.class, new GraphDocumentReadOptions()).get(); + assertThat(res).isNull(); + } + + @Test + void deleteVertexIfMatchFail() throws InterruptedException, ExecutionException { + final BaseDocument doc = new BaseDocument(UUID.randomUUID().toString()); + final VertexEntity createResult = db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).insertVertex(doc, null) + .get(); + final VertexDeleteOptions options = new VertexDeleteOptions().ifMatch("no"); + try { + db.graph(GRAPH_NAME).vertexCollection(COLLECTION_NAME).deleteVertex(createResult.getKey(), options).get(); + fail(); + } catch (final ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoViewTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoViewTest.java new file mode 100644 index 000000000..8bc81b238 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ArangoViewTest.java @@ -0,0 +1,86 @@ +/* + * 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.async; + +import com.arangodb.entity.ViewEntity; +import com.arangodb.entity.ViewType; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Mark Vollmary + */ + +class ArangoViewTest extends BaseTest { + + private static final String VIEW_NAME = "view_test"; + + @BeforeAll + static void setup() throws InterruptedException, ExecutionException { + if (!isAtLeastVersion(arangoDB, 3, 4)) + return; + db.createView(VIEW_NAME, ViewType.ARANGO_SEARCH).get(); + } + + @Test + void exists() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + assertThat(db.view(VIEW_NAME).exists().get()).isTrue(); + } + + @Test + void getInfo() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final ViewEntity info = db.view(VIEW_NAME).getInfo().get(); + assertThat(info).isNotNull(); + assertThat(info.getId()).isNotNull(); + assertThat(info.getName()).isEqualTo(VIEW_NAME); + assertThat(info.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + } + + @Test + void drop() throws InterruptedException, ExecutionException { + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_droptest"; + db.createView(name, ViewType.ARANGO_SEARCH).get(); + final ArangoViewAsync view = db.view(name); + view.drop().get(); + assertThat(view.exists().get()).isFalse(); + } + + @Test + void rename() throws InterruptedException, ExecutionException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 4)); + final String name = VIEW_NAME + "_renametest"; + final String newName = name + "_new"; + db.createView(name, ViewType.ARANGO_SEARCH).get(); + db.view(name).rename(newName).get(); + assertThat(db.view(name).exists().get()).isFalse(); + assertThat(db.view(newName).exists().get()).isTrue(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/BaseTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/BaseTest.java new file mode 100644 index 000000000..729596e3c --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/BaseTest.java @@ -0,0 +1,118 @@ +/* + * 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.async; + +import com.arangodb.DbName; +import com.arangodb.entity.ArangoDBEngine; +import com.arangodb.entity.License; +import com.arangodb.entity.ServerRole; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import com.arangodb.util.TestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + + +/** + * @author Mark Vollmary + */ +public abstract class BaseTest { + + static final DbName TEST_DB = DbName.of("java_driver_test_db"); + static ArangoDBAsync arangoDB; + static ArangoDatabaseAsync db; + + @BeforeAll + static void init() throws InterruptedException, ExecutionException { + if (arangoDB == null) { + arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + } + + if (arangoDB.db(TEST_DB).exists().get()) { + arangoDB.db(TEST_DB).drop().get(); + } + + arangoDB.createDatabase(TEST_DB).get(); + BaseTest.db = arangoDB.db(TEST_DB); + } + + @AfterAll + static void shutdown() throws InterruptedException, ExecutionException { + arangoDB.db(TEST_DB).drop().get(); + arangoDB.shutdown(); + arangoDB = null; + } + + static String rnd() { + return UUID.randomUUID().toString(); + } + + protected static boolean isAtLeastVersion(final ArangoDBAsync arangoDB, final int major, final int minor, + final int patch) + throws InterruptedException, ExecutionException { + return TestUtils.isAtLeastVersion(arangoDB.getVersion().get().getVersion(), major, minor, + patch); + } + + protected static boolean isAtLeastVersion(final ArangoDBAsync arangoDB, final int major, final int minor) + throws InterruptedException, ExecutionException { + return isAtLeastVersion(arangoDB, major, minor, 0); + } + + protected boolean isAtLeastVersion(final int major, final int minor, final int patch) throws InterruptedException + , ExecutionException { + return isAtLeastVersion(arangoDB, major, minor, patch); + } + + protected boolean isAtLeastVersion(final int major, final int minor) throws InterruptedException, + ExecutionException { + return isAtLeastVersion(major, minor, 0); + } + + boolean isLessThanVersion(final int major, final int minor) throws ExecutionException, InterruptedException { + return isLessThanVersion(major, minor, 0); + } + + boolean isLessThanVersion(final int major, final int minor, final int patch) throws ExecutionException, InterruptedException { + return TestUtils.isLessThanVersion(db.getVersion().get().getVersion(), major, minor, patch); + } + + boolean isStorageEngine(ArangoDBEngine.StorageEngineName name) throws ExecutionException, InterruptedException { + return name.equals(db.getEngine().get().getName()); + } + + boolean isSingleServer() throws ExecutionException, InterruptedException { + return (arangoDB.getRole().get() == ServerRole.SINGLE); + } + + boolean isCluster() throws ExecutionException, InterruptedException { + return arangoDB.getRole().get() == ServerRole.COORDINATOR; + } + + boolean isEnterprise() throws ExecutionException, InterruptedException { + return arangoDB.getVersion().get().getLicense() == License.ENTERPRISE; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/CommunicationTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/CommunicationTest.java new file mode 100644 index 000000000..0b2b6c962 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/CommunicationTest.java @@ -0,0 +1,46 @@ +/* + * 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.async; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CompletableFuture; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +class CommunicationTest { + + @Test + @Disabled + void disconnect() { + final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder().build(); + final CompletableFuture> result = arangoDB.db().query("return sleep(1)", null, null, + null); + arangoDB.shutdown(); + assertThat(result.isCompletedExceptionally()).isEqualTo(true); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ConcurrencyTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ConcurrencyTest.java new file mode 100644 index 000000000..f0643c2ca --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ConcurrencyTest.java @@ -0,0 +1,94 @@ +/* + * 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.async; + + +import com.arangodb.async.internal.ArangoExecutorAsync; +import com.arangodb.entity.ArangoDBVersion; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.fail; + + +/** + * @author Michele Rastelli + */ +class ConcurrencyTest { + + private ArangoDBAsync arangoDB; + + @BeforeEach + void initialize() { + arangoDB = new ArangoDBAsync.Builder().build(); + } + + /** + * FIXME: make the user executor configurable in com.arangodb.internal.ArangoExecutorAsync::execute + * (eg. this test passes using a CachedThreadPool) + */ + @Disabled + @Test + @Timeout(2) + void executorLimit() { + List> futures = IntStream.range(0, 20) + .mapToObj(i -> arangoDB.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(); + } + }); + } + + + /** + * outgoing requests should be queued in the {@link ArangoExecutorAsync} outgoingExecutor + */ + @Disabled + @Test + @Timeout(1) + void outgoingRequestsParallelismTest() { + for (int i = 0; i < 50_000; i++) { + arangoDB.getVersion(); + } + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ConcurrencyTests.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ConcurrencyTests.java new file mode 100644 index 000000000..504c550a7 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/ConcurrencyTests.java @@ -0,0 +1,29 @@ +package com.arangodb.async; + + +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +class ConcurrencyTests { + + @Test + void concurrentPendingRequests() throws ExecutionException, InterruptedException { + ArangoDBAsync adb = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + List>> reqs = IntStream.range(0, 10) + .mapToObj(__ -> adb.db().query("RETURN SLEEP(1)", Void.class)) + .collect(Collectors.toList()); + for (CompletableFuture> req : reqs) { + req.get(); + } + adb.shutdown(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/InvertedIndexTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/InvertedIndexTest.java new file mode 100644 index 000000000..8ee509526 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/InvertedIndexTest.java @@ -0,0 +1,199 @@ +package com.arangodb.async; + +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.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class InvertedIndexTest extends BaseTest { + + private static final String COLLECTION_NAME = "InvertedIndexTestAsync_collection"; + + InvertedIndexTest() throws ExecutionException, InterruptedException { + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + if (!collection.exists().get()) { + collection.create().get(); + } + } + + @BeforeAll + static void setup() throws InterruptedException, ExecutionException { + db.createCollection(COLLECTION_NAME, null).get(); + } + + @AfterEach + void teardown() throws InterruptedException, ExecutionException { + db.collection(COLLECTION_NAME).drop().get(); + } + + + 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) throws ExecutionException, InterruptedException { + InvertedIndexField field = new InvertedIndexField() + .name("foo") + .analyzer(AnalyzerType.identity.toString()) + .includeAllFields(true) + .searchField(false) + .trackListPositions(false) + .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("invertedIndex-" + UUID.randomUUID()) + .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) + ) + .storedValues(new StoredValue(Arrays.asList("f3", "f4"), ArangoSearchCompression.none)) + .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.BYTES_ACCUM).threshold(1.)) + .writebufferIdle(44L) + .writebufferActive(55L) + .writebufferSizeMax(66L); + } + + 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()); + } + + @Test + void createAndGetInvertedIndex() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + 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); + } + + @Test + void getInvertedIndexesShouldNotReturnOtherIndexTypes() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // 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()); + } + + @Test + void getIndexesShouldNotReturnInvertedIndexes() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 10)); + + ArangoCollectionAsync collection = db.collection(COLLECTION_NAME); + + // 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/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/JwtAuthTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/JwtAuthTest.java new file mode 100644 index 000000000..f821f3ea9 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/JwtAuthTest.java @@ -0,0 +1,103 @@ +package com.arangodb.async; + +import com.arangodb.*; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +class JwtAuthTest { + + private static String jwt; + private ArangoDBAsync arangoDB; + + @BeforeAll + static void init() { + ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .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(DbName.SYSTEM) + .method(Request.Method.POST) + .path("/_open/auth") + .body(reqBody) + .build(); + + Response resp = arangoDB.execute(req, Map.class); + return (String) resp.getBody().get("jwt"); + } + + @AfterEach + void after() { + if (arangoDB != null) + arangoDB.shutdown(); + } + + @Test + void notAuthenticated() throws InterruptedException { + arangoDB = getBuilder().build(); + try { + arangoDB.getVersion().get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) e.getCause()).getResponseCode()).isEqualTo(401); + } + arangoDB.shutdown(); + } + + @Test + void authenticated() throws ExecutionException, InterruptedException { + arangoDB = getBuilder() + .jwt(jwt) + .build(); + arangoDB.getVersion().get(); + arangoDB.shutdown(); + } + + @Test + @Disabled("DE-423") + void updateJwt() throws ExecutionException, InterruptedException { + arangoDB = getBuilder() + .jwt(jwt) + .build(); + arangoDB.getVersion().get(); + arangoDB.updateJwt("bla"); + try { + arangoDB.getVersion().get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + assertThat(((ArangoDBException) e.getCause()).getResponseCode()).isEqualTo(401); + } + + arangoDB.updateJwt(jwt); + arangoDB.getVersion().get(); + arangoDB.shutdown(); + } + + private ArangoDBAsync.Builder getBuilder() { + return new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .jwt(null) // unset credentials from properties file + .user(null) // unset credentials from properties file + .password(null); // unset credentials from properties file + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionConflictsTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionConflictsTest.java new file mode 100644 index 000000000..659781ae2 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionConflictsTest.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.async; + +import com.arangodb.ArangoDBException; +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.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Michele Rastelli + */ +class StreamTransactionConflictsTest extends BaseTest { + + private static final String COLLECTION_NAME = "db_concurrent_stream_transactions_test"; + + public StreamTransactionConflictsTest() throws ExecutionException, InterruptedException { + if (db.collection(COLLECTION_NAME).exists().get()) + db.collection(COLLECTION_NAME).drop().get(); + + db.createCollection(COLLECTION_NAME, null).get(); + } + + @AfterEach + void teardown() throws ExecutionException, InterruptedException { + if (db.collection(COLLECTION_NAME).exists().get()) + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void conflictOnInsertDocumentWithNotYetCommittedTx() throws ExecutionException, InterruptedException { + 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(); + + try { + // insert conflicting document from within tx2 + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(key), + new DocumentCreateOptions().streamTransactionId(tx2.getId())).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + + db.abortStreamTransaction(tx1.getId()).get(); + db.abortStreamTransaction(tx2.getId()).get(); + } + + @Test + void conflictOnInsertDocumentWithAlreadyCommittedTx() throws ExecutionException, InterruptedException { + 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(); + + try { + // insert conflicting document from within tx2 + db.collection(COLLECTION_NAME).insertDocument(new BaseDocument(key), + new DocumentCreateOptions().streamTransactionId(tx2.getId())).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + + db.abortStreamTransaction(tx2.getId()).get(); + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionGraphTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionGraphTest.java new file mode 100644 index 000000000..60ee6ea11 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionGraphTest.java @@ -0,0 +1,398 @@ +/* + * 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.async; + + +import com.arangodb.entity.*; +import com.arangodb.model.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Michele Rastelli + */ +class StreamTransactionGraphTest extends BaseTest { + + 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 final ArangoGraphAsync graph; + private final ArangoVertexCollectionAsync vertexCollection1; + private final ArangoVertexCollectionAsync vertexCollection2; + private final ArangoEdgeCollectionAsync edgeCollection; + + public StreamTransactionGraphTest() throws ExecutionException, InterruptedException { + + graph = db.graph(GRAPH_NAME); + + if (graph.exists().get()) + graph.drop().get(); + + graph.create(Collections.singletonList(new EdgeDefinition().collection(EDGE_COLLECTION).from(VERTEX_COLLECTION_1).to(VERTEX_COLLECTION_2))).get(); + + vertexCollection1 = graph.vertexCollection(VERTEX_COLLECTION_1); + vertexCollection2 = graph.vertexCollection(VERTEX_COLLECTION_2); + edgeCollection = graph.edgeCollection(EDGE_COLLECTION); + } + + @AfterEach + void teardown() throws ExecutionException, InterruptedException { + if (graph.exists().get()) + graph.drop().get(); + if (db.collection(EDGE_COLLECTION).exists().get()) + db.collection(EDGE_COLLECTION).drop().get(); + if (db.collection(VERTEX_COLLECTION_1).exists().get()) + db.collection(VERTEX_COLLECTION_1).drop().get(); + if (db.collection(VERTEX_COLLECTION_2).exists().get()) + db.collection(VERTEX_COLLECTION_2).drop().get(); + } + + private BaseEdgeDocument createEdgeValue(String streamTransactionId) throws ExecutionException, + InterruptedException { + final VertexEntity v1 = vertexCollection1.insertVertex(new BaseDocument(), + new VertexCreateOptions().streamTransactionId(streamTransactionId)).get(); + final VertexEntity v2 = vertexCollection2.insertVertex(new BaseDocument(), + new VertexCreateOptions().streamTransactionId(streamTransactionId)).get(); + final BaseEdgeDocument value = new BaseEdgeDocument(); + value.setFrom(v1.getId()); + value.setTo(v2.getId()); + return value; + } + + @Test + void getVertex() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + 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(); + } + + + @Test + void createVertex() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + 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(); + } + + @Test + void replaceVertex() 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(); + + 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"); + } + + @Test + void updateVertex() 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(); + + 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"); + } + + @Test + void deleteVertex() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + VertexEntity createdDoc = vertexCollection1.insertVertex(new BaseDocument(), null).get(); + + 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(); + } + + + @Test + void getEdge() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + 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)).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(); + } + + + @Test + void createEdge() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + 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()), + 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(); + } + + @Test + void replaceEdge() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseEdgeDocument doc = createEdgeValue(null); + doc.addAttribute("test", "foo"); + + EdgeEntity createdEdge = edgeCollection.insertEdge(doc, null).get(); + + 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"); + } + + @Test + void updateEdge() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + BaseEdgeDocument doc = createEdgeValue(null); + doc.addAttribute("test", "foo"); + + EdgeEntity createdDoc = edgeCollection.insertEdge(doc, null).get(); + + 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"); + } + + @Test + void deleteEdge() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + EdgeEntity createdDoc = edgeCollection.insertEdge(createEdgeValue(null), null).get(); + + 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(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionTest.java new file mode 100644 index 000000000..658bab18b --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/StreamTransactionTest.java @@ -0,0 +1,362 @@ +/* + * 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.async; + +import com.arangodb.ArangoDBException; +import com.arangodb.entity.*; +import com.arangodb.model.DocumentCreateOptions; +import com.arangodb.model.DocumentReadOptions; +import com.arangodb.model.StreamTransactionOptions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Michele Rastelli + */ +class StreamTransactionTest extends BaseTest { + + private static final String COLLECTION_NAME = "db_stream_transaction_test"; + + public StreamTransactionTest() throws ExecutionException, InterruptedException { + if (db.collection(COLLECTION_NAME).exists().get()) + db.collection(COLLECTION_NAME).drop().get(); + + db.createCollection(COLLECTION_NAME, null).get(); + } + + @AfterEach + void teardown() throws ExecutionException, InterruptedException { + if (db.collection(COLLECTION_NAME).exists().get()) + db.collection(COLLECTION_NAME).drop().get(); + } + + @Test + void beginStreamTransaction() 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(); + } + + @Test + void beginStreamTransactionWithNonExistingCollectionsShouldThrow() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.beginStreamTransaction(new StreamTransactionOptions().writeCollections("notExistingCollection")).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void abortStreamTransaction() 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); + } + + @Test + void abortStreamTransactionTwice() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity begunTx = db.beginStreamTransaction(null).get(); + db.abortStreamTransaction(begunTx.getId()).get(); + db.abortStreamTransaction(begunTx.getId()).get(); + } + + @Test + void abortStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow() throws ExecutionException, + InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.abortStreamTransaction("000000").get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void abortStreamTransactionWithInvalidTransactionIdShouldThrow() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.abortStreamTransaction("invalidTransactionId").get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void abortCommittedStreamTransactionShouldThrow() 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(); + + try { + db.abortStreamTransaction(createdTx.getId()).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void getStreamTransaction() 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(); + } + + @Test + void getStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow() throws ExecutionException, + InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.getStreamTransaction("000000").get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void getStreamTransactionWithInvalidTransactionIdShouldThrow() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.getStreamTransaction("invalidTransactionId").get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void commitStreamTransaction() 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); + } + + @Test + void commitStreamTransactionTwice() 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(); + db.commitStreamTransaction(createdTx.getId()).get(); + } + + @Test + void commitStreamTransactionWhenTransactionIdDoesNotExistsShouldThrow() throws ExecutionException, + InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.commitStreamTransaction("000000").get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void commitStreamTransactionWithInvalidTransactionIdShouldThrow() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.commitStreamTransaction("invalidTransactionId").get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void commitAbortedStreamTransactionShouldThrow() 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(); + + try { + db.commitStreamTransaction(createdTx.getId()).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void getDocument() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + StreamTransactionEntity tx = db + .beginStreamTransaction(new StreamTransactionOptions().readCollections(COLLECTION_NAME)).get(); + + // insert a document from outside the tx + DocumentCreateEntity externalDoc = db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), null).get(); + + // assert that the document is not found from within the tx + assertThat(db.collection(COLLECTION_NAME).getDocument(externalDoc.getKey(), BaseDocument.class, + new DocumentReadOptions().streamTransactionId(tx.getId())).get()).isNull(); + + db.abortStreamTransaction(tx.getId()).get(); + } + + @Test + void getDocumentWithNonExistingTransactionIdShouldThrow() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.collection(COLLECTION_NAME) + .getDocument("docId", BaseDocument.class, new DocumentReadOptions().streamTransactionId("123456")) + .get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void insertDocumentWithNonExistingTransactionIdShouldThrow() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.collection(COLLECTION_NAME) + .insertDocument(new BaseDocument(), new DocumentCreateOptions().streamTransactionId("123456")).get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void getDocumentWithInvalidTransactionIdShouldThrow() throws ExecutionException, InterruptedException { + assumeTrue(isSingleServer()); + assumeTrue(isAtLeastVersion(3, 5)); + assumeTrue(isStorageEngine(ArangoDBEngine.StorageEngineName.rocksdb)); + + try { + db.collection(COLLECTION_NAME) + .getDocument("docId", BaseDocument.class, new DocumentReadOptions().streamTransactionId("abcde")) + .get(); + fail(); + } catch (ExecutionException e) { + assertThat(e.getCause()).isInstanceOf(ArangoDBException.class); + } + } + + @Test + void getStreamTransactions() 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(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/debug/ConsolidationIntervalMsecTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/debug/ConsolidationIntervalMsecTest.java new file mode 100644 index 000000000..5a3d0ddad --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/debug/ConsolidationIntervalMsecTest.java @@ -0,0 +1,83 @@ +/* + * 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.async.debug; + +import com.arangodb.DbName; +import com.arangodb.async.ArangoDBAsync; +import com.arangodb.async.ArangoDatabaseAsync; +import com.arangodb.async.BaseTest; +import com.arangodb.entity.ViewEntity; +import com.arangodb.entity.ViewType; +import com.arangodb.entity.arangosearch.ArangoSearchPropertiesEntity; +import com.arangodb.entity.arangosearch.CollectionLink; +import com.arangodb.entity.arangosearch.FieldLink; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import com.arangodb.model.arangosearch.ArangoSearchCreateOptions; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author Michele Rastelli + *

+ * https://github.com/arangodb/arangodb-java-driver-async/issues/15 + */ +class ConsolidationIntervalMsecTest extends BaseTest { + + @Test + void consolidationIntervalMsec() throws ExecutionException, InterruptedException { + assumeTrue(isAtLeastVersion(3, 4)); + + ArangoDBAsync arango = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .user("root") + .password("test") + .build(); + + ArangoDatabaseAsync db = arango.db(DbName.of("database_of_things")); + if (db.exists().join()) { + db.drop().join(); + } + + db.create().join(); + db.collection("Thing").create().join(); + + ViewEntity result = db.createArangoSearch("ThingsSearchView", new ArangoSearchCreateOptions() + .consolidationIntervalMsec(60000L) //<== This line breaks it + .link(CollectionLink.on("Thing") + .fields(FieldLink.on("name") + .analyzers("identity")))) + .join(); + + assertThat(result.getName()).isEqualTo("ThingsSearchView"); + assertThat(result.getType()).isEqualTo(ViewType.ARANGO_SEARCH); + + ArangoSearchPropertiesEntity props = db.arangoSearch("ThingsSearchView").getProperties().join(); + assertThat(props.getName()).isEqualTo("ThingsSearchView"); + assertThat(props.getConsolidationIntervalMsec()).isEqualTo(60000L); + assertThat(props.getLinks().iterator().hasNext()).isTrue(); + assertThat(props.getLinks().iterator().next().getName()).isEqualTo("Thing"); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/ExampleBase.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/ExampleBase.java new file mode 100644 index 000000000..9c1ce81e2 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/ExampleBase.java @@ -0,0 +1,64 @@ +/* + * 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.async.example; + +import com.arangodb.DbName; +import com.arangodb.async.ArangoCollectionAsync; +import com.arangodb.async.ArangoDBAsync; +import com.arangodb.async.ArangoDatabaseAsync; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +import java.util.concurrent.ExecutionException; + +/** + * @author Mark Vollmary + */ +public class ExampleBase { + + protected static final String COLLECTION_NAME = "json_example_collection"; + private static final DbName DB_NAME = DbName.of("json_example_db"); + protected static ArangoDatabaseAsync db; + protected static ArangoCollectionAsync collection; + private static ArangoDBAsync arangoDB; + + @BeforeAll + static void setUp() throws InterruptedException, ExecutionException { + arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + if (arangoDB.db(DB_NAME).exists().get()) { + arangoDB.db(DB_NAME).drop().get(); + } + arangoDB.createDatabase(DB_NAME).get(); + db = arangoDB.db(DB_NAME); + db.createCollection(COLLECTION_NAME).get(); + collection = db.collection(COLLECTION_NAME); + } + + @AfterAll + static void tearDown() throws InterruptedException, ExecutionException { + db.drop().get(); + arangoDB.shutdown(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/AqlQueryWithSpecialReturnTypesExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/AqlQueryWithSpecialReturnTypesExampleTest.java new file mode 100644 index 000000000..9ab6624df --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/AqlQueryWithSpecialReturnTypesExampleTest.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.async.example.document; + +import com.arangodb.async.example.ExampleBase; +import com.arangodb.entity.BaseDocument; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +class AqlQueryWithSpecialReturnTypesExampleTest extends ExampleBase { + + @BeforeAll + static void before() throws InterruptedException, ExecutionException { + createExamples(); + } + + private static void createExamples() throws InterruptedException, ExecutionException { + for (int i = 0; i < 100; i++) { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.addAttribute("name", "TestUser" + i); + value.addAttribute("gender", (i % 2) == 0 ? Gender.MALE : Gender.FEMALE); + value.addAttribute("age", i + 10); + db.collection(COLLECTION_NAME).insertDocument(value).get(); + } + } + + @Test + void aqlWithLimitQueryAsJsonObject() throws InterruptedException, ExecutionException { + final String query = "FOR t IN " + COLLECTION_NAME + + " FILTER t.age >= 20 && t.age < 30 && t.gender == @gender RETURN t"; + final Map bindVars = Collections.singletonMap("gender", Gender.FEMALE); + db.query(query, bindVars, null, ObjectNode.class) + .whenComplete((cursor, ex) -> cursor.forEachRemaining(node -> { + assertThat(node.get("name").asText()).isIn("TestUser11", "TestUser13", "TestUser15", "TestUser17" + , "TestUser19"); + assertThat(node.get("gender").asText()).isEqualTo(Gender.FEMALE.name()); + assertThat(node.get("age").asInt()).isIn(21, 23, 25, 27, 29); + })) + .get(); + } + + @Test + void aqlWithLimitQueryAsArrayNode() throws InterruptedException, ExecutionException { + final String query = "FOR t IN " + COLLECTION_NAME + + " FILTER t.age >= 20 && t.age < 30 && t.gender == @gender RETURN [t.name, t.gender, t.age]"; + final Map bindVars = Collections.singletonMap("gender", Gender.FEMALE); + db.query(query, bindVars, null, ArrayNode.class) + .whenComplete((cursor, ex) -> cursor.forEachRemaining(arrNode -> { + assertThat(arrNode.get(0).asText()).isIn("TestUser11", "TestUser13", "TestUser15", "TestUser17", + "TestUser19"); + assertThat(arrNode.get(1).asText()).isEqualTo(Gender.FEMALE.name()); + assertThat(arrNode.get(2).asInt()).isIn(21, 23, 25, 27, 29); + })) + .get(); + } + + @Test + void aqlWithLimitQueryAsMap() throws InterruptedException, ExecutionException { + final String query = "FOR t IN " + COLLECTION_NAME + + " FILTER t.age >= 20 && t.age < 30 && t.gender == @gender RETURN t"; + final Map bindVars = Collections.singletonMap("gender", Gender.FEMALE); + db.query(query, bindVars, null, Map.class) + .whenComplete((cursor, ex) -> cursor.forEachRemaining(map -> { + assertThat(map.get("name")).isNotNull(); + assertThat(String.valueOf(map.get("name"))).isIn("TestUser11", "TestUser13", "TestUser15", + "TestUser17", "TestUser19"); + assertThat(map.get("gender")).isNotNull(); + assertThat(String.valueOf(map.get("gender"))).isEqualTo(Gender.FEMALE.name()); + assertThat(map.get("age")).isNotNull(); + assertThat(Long.valueOf(map.get("age").toString())).isIn(21L, 23L, 25L, 27L, 29L); + })) + .get(); + } + + @Test + void aqlWithLimitQueryAsList() throws InterruptedException, ExecutionException { + final String query = "FOR t IN " + COLLECTION_NAME + + " FILTER t.age >= 20 && t.age < 30 && t.gender == @gender RETURN [t.name, t.gender, t.age]"; + final Map bindVars = Collections.singletonMap("gender", Gender.FEMALE); + db.query(query, bindVars, null, List.class) + .whenComplete((cursor, ex) -> cursor.forEachRemaining(list -> { + assertThat(list.get(0)).isNotNull(); + assertThat(String.valueOf(list.get(0))).isIn("TestUser11", "TestUser13", "TestUser15", + "TestUser17", "TestUser19"); + assertThat(list.get(1)).isNotNull(); + assertThat(Gender.valueOf(String.valueOf(list.get(1)))).isEqualTo(Gender.FEMALE); + assertThat(list.get(2)).isNotNull(); + assertThat(Long.valueOf(String.valueOf(list.get(2)))).isIn(21L, 23L, 25L, 27L, 29L); + })) + .get(); + } + + public enum Gender { + MALE, FEMALE + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/GetDocumentExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/GetDocumentExampleTest.java new file mode 100644 index 000000000..11ef3c63f --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/GetDocumentExampleTest.java @@ -0,0 +1,94 @@ +/* + * 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.async.example.document; + +import com.arangodb.async.example.ExampleBase; +import com.arangodb.entity.BaseDocument; +import com.arangodb.util.RawJson; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +class GetDocumentExampleTest extends ExampleBase { + + private static String key = null; + + @BeforeAll + static void before() throws InterruptedException, ExecutionException { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.addAttribute("foo", "bar"); + key = collection.insertDocument(value).get().getKey(); + } + + @Test + void getAsBean() throws InterruptedException, ExecutionException { + collection.getDocument(key, TestEntity.class) + .whenComplete((doc, ex) -> { + assertThat(doc).isNotNull(); + assertThat(doc.getFoo()).isEqualTo("bar"); + }) + .get(); + } + + @Test + void getAsBaseDocument() throws InterruptedException, ExecutionException { + collection.getDocument(key, BaseDocument.class) + .whenComplete((doc, ex) -> { + assertThat(doc).isNotNull(); + assertThat(doc.getAttribute("foo")).isNotNull(); + assertThat(String.valueOf(doc.getAttribute("foo"))).isEqualTo("bar"); + }) + .get(); + } + + @Test + void getAsVPack() throws InterruptedException, ExecutionException { + collection.getDocument(key, JsonNode.class) + .whenComplete((doc, ex) -> { + assertThat(doc).isNotNull(); + assertThat(doc.get("foo").isTextual()).isEqualTo(true); + assertThat(doc.get("foo").asText()).isEqualTo("bar"); + }) + .get(); + } + + @Test + void getAsJson() throws InterruptedException, ExecutionException { + collection.getDocument(key, RawJson.class) + .whenComplete((doc, ex) -> { + assertThat(doc.getValue()) + .isNotNull() + .contains("foo") + .contains("bar"); + }) + .get(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/ImportDocumentExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/ImportDocumentExampleTest.java new file mode 100644 index 000000000..dacf1bd87 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/ImportDocumentExampleTest.java @@ -0,0 +1,87 @@ +/* + * 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.async.example.document; + +import com.arangodb.async.example.ExampleBase; +import com.arangodb.entity.DocumentImportEntity; +import com.arangodb.model.DocumentImportOptions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author Michele Rastelli + */ +class ImportDocumentExampleTest extends ExampleBase { + + private static final int MAX_PENDING_REQUESTS = 10; + + @Test + void importDocument() { + AtomicLong pendingReqsCount = new AtomicLong(); + + Stream> chunks = IntStream.range(0, 100) + .mapToObj(i -> IntStream.range(0, 500) + .mapToObj(it -> new TestEntity(UUID.randomUUID().toString())).collect(Collectors.toList()) + ); + + List> completableFutures = chunks + .map(p -> { + // wait for pending requests + while (pendingReqsCount.get() > MAX_PENDING_REQUESTS) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + pendingReqsCount.incrementAndGet(); + return collection.importDocuments(p, new DocumentImportOptions()) + .thenApply(it -> { + pendingReqsCount.decrementAndGet(); + return it; + }); + } + ) + .collect(Collectors.toList()); + + completableFutures.forEach(cf -> { + try { + cf.get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + fail(); + } + }); + + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/InsertDocumentExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/InsertDocumentExampleTest.java new file mode 100644 index 000000000..e55eda546 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/InsertDocumentExampleTest.java @@ -0,0 +1,74 @@ +/* + * 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.async.example.document; + +import com.arangodb.async.example.ExampleBase; +import com.arangodb.entity.BaseDocument; +import com.arangodb.util.RawJson; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +class InsertDocumentExampleTest extends ExampleBase { + + @Test + void insertBean() throws ExecutionException, InterruptedException { + collection.insertDocument(new TestEntity("bar")) + .whenComplete((doc, ex) -> assertThat(doc.getKey()).isNotNull()) + .get(); + } + + @Test + void insertBaseDocument() throws ExecutionException, InterruptedException { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.addAttribute("foo", "bar"); + collection.insertDocument(value) + .whenComplete((doc, ex) -> assertThat(doc.getKey()).isNotNull()) + .get(); + } + + @Test + void insertJsonNode() throws ExecutionException, InterruptedException { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode node = mapper.createObjectNode(); + node.put("foo", "bar"); + collection.insertDocument(node) + .whenComplete((doc, ex) -> assertThat(doc.getKey()).isNotNull()) + .get(); + } + + @Test + void insertJson() throws ExecutionException, InterruptedException { + collection.insertDocument(RawJson.of("{\"foo\":\"bar\"}")) + .whenComplete((doc, ex) -> assertThat(doc.getKey()).isNotNull()) + .get(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/TestEntity.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/TestEntity.java new file mode 100644 index 000000000..2ef89c19f --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/document/TestEntity.java @@ -0,0 +1,47 @@ +/* + * 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.async.example.document; + +/** + * @author Mark Vollmary + */ +public class TestEntity { + + private String foo; + + public TestEntity() { + super(); + } + + public TestEntity(final String foo) { + super(); + this.foo = foo; + } + + public String getFoo() { + return foo; + } + + void setFoo(final String foo) { + this.foo = foo; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/AQLActorsAndMoviesExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/AQLActorsAndMoviesExampleTest.java new file mode 100644 index 000000000..259cc4451 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/AQLActorsAndMoviesExampleTest.java @@ -0,0 +1,605 @@ +/* + * 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.async.example.graph; + +import com.arangodb.DbName; +import com.arangodb.async.ArangoCollectionAsync; +import com.arangodb.async.ArangoCursorAsync; +import com.arangodb.async.ArangoDBAsync; +import com.arangodb.async.ArangoDatabaseAsync; +import com.arangodb.entity.BaseDocument; +import com.arangodb.entity.BaseEdgeDocument; +import com.arangodb.entity.CollectionType; +import com.arangodb.entity.DocumentCreateEntity; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import com.arangodb.model.CollectionCreateOptions; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + * @see + * AQL Example Queries on an + * Actors and Movies Database + */ +class AQLActorsAndMoviesExampleTest { + + private static final DbName TEST_DB = DbName.of("actors_movies_test_db"); + private static ArangoDBAsync arangoDB; + private static ArangoDatabaseAsync db; + + @BeforeAll + static void setUp() throws InterruptedException, ExecutionException { + arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + if (arangoDB.db(TEST_DB).exists().get()) { + arangoDB.db(TEST_DB).drop().get(); + } + arangoDB.createDatabase(TEST_DB).get(); + db = arangoDB.db(TEST_DB); + createData(); + } + + @AfterAll + static void tearDown() throws InterruptedException, ExecutionException { + db.drop().get(); + arangoDB.shutdown(); + } + + private static DocumentCreateEntity saveMovie( + final ArangoCollectionAsync movies, + final String key, + final String title, + final int released, + final String tagline) throws InterruptedException, ExecutionException { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.setKey(key); + value.addAttribute("title", title); + value.addAttribute("released", released); + value.addAttribute("tagline", tagline); + return movies.insertDocument(value).get(); + } + + private static DocumentCreateEntity saveActor( + final ArangoCollectionAsync actors, + final String key, + final String name, + final int born) throws InterruptedException, ExecutionException { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.setKey(key); + value.addAttribute("name", name); + value.addAttribute("born", born); + return actors.insertDocument(value).get(); + } + + private static void saveActsIn( + final ArangoCollectionAsync actsIn, + final String actor, + final String movie, + final String[] roles, + final int year) throws InterruptedException, ExecutionException { + final BaseEdgeDocument value = new BaseEdgeDocument(); + value.setFrom(actor); + value.setTo(movie); + value.addAttribute("roles", roles); + value.addAttribute("year", year); + actsIn.insertDocument(value).get(); + } + + private static void createData() throws InterruptedException, ExecutionException { + db.createCollection("actors").get(); + final ArangoCollectionAsync actors = db.collection("actors"); + db.createCollection("movies").get(); + final ArangoCollectionAsync movies = db.collection("movies"); + db.createCollection("actsIn", new CollectionCreateOptions().type(CollectionType.EDGES)).get(); + final ArangoCollectionAsync actsIn = db.collection("actsIn"); + + final String theMatrix = saveMovie(movies, "TheMatrix", "The Matrix", 1999, "Welcome to the Real World") + .getId(); + final String keanu = saveActor(actors, "Keanu", "Keanu Reeves", 1964).getId(); + final String carrie = saveActor(actors, "Carrie", "Carrie-Anne Moss", 1967).getId(); + final String laurence = saveActor(actors, "Laurence", "Laurence Fishburne", 1961).getId(); + final String hugo = saveActor(actors, "Hugo", "Hugo Weaving", 1960).getId(); + final String emil = saveActor(actors, "Emil", "Emil Eifrem", 1978).getId(); + + saveActsIn(actsIn, keanu, theMatrix, new String[]{"Neo"}, 1999); + saveActsIn(actsIn, carrie, theMatrix, new String[]{"Trinity"}, 1999); + saveActsIn(actsIn, laurence, theMatrix, new String[]{"Morpheus"}, 1999); + saveActsIn(actsIn, hugo, theMatrix, new String[]{"Agent Smith"}, 1999); + saveActsIn(actsIn, emil, theMatrix, new String[]{"Emil"}, 1999); + + final String theMatrixReloaded = saveMovie(movies, "TheMatrixReloaded", "The Matrix Reloaded", 2003, + "Free your mind").getId(); + saveActsIn(actsIn, keanu, theMatrixReloaded, new String[]{"Neo"}, 2003); + saveActsIn(actsIn, carrie, theMatrixReloaded, new String[]{"Trinity"}, 2003); + saveActsIn(actsIn, laurence, theMatrixReloaded, new String[]{"Morpheus"}, 2003); + saveActsIn(actsIn, hugo, theMatrixReloaded, new String[]{"Agent Smith"}, 2003); + + final String theMatrixRevolutions = saveMovie(movies, "TheMatrixRevolutions", "The Matrix Revolutions", 2003, + "Everything that has a beginning has an end").getId(); + saveActsIn(actsIn, keanu, theMatrixRevolutions, new String[]{"Neo"}, 2003); + saveActsIn(actsIn, carrie, theMatrixRevolutions, new String[]{"Trinity"}, 2003); + saveActsIn(actsIn, laurence, theMatrixRevolutions, new String[]{"Morpheus"}, 2003); + saveActsIn(actsIn, hugo, theMatrixRevolutions, new String[]{"Agent Smith"}, 2003); + + final String theDevilsAdvocate = saveMovie(movies, "TheDevilsAdvocate", "The Devil's Advocate", 1997, + "Evil has its winning ways").getId(); + final String charlize = saveActor(actors, "Charlize", "Charlize Theron", 1975).getId(); + final String al = saveActor(actors, "Al", "Al Pacino", 1940).getId(); + saveActsIn(actsIn, keanu, theDevilsAdvocate, new String[]{"Kevin Lomax"}, 1997); + saveActsIn(actsIn, charlize, theDevilsAdvocate, new String[]{"Mary Ann Lomax"}, 1997); + saveActsIn(actsIn, al, theDevilsAdvocate, new String[]{"John Milton"}, 1997); + + final String AFewGoodMen = saveMovie(movies, "AFewGoodMen", "A Few Good Men", 1992, + "In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at " + + "nothing to keep his honor, and one will stop at nothing to find the truth.") + .getId(); + final String tomC = saveActor(actors, "TomC", "Tom Cruise", 1962).getId(); + final String jackN = saveActor(actors, "JackN", "Jack Nicholson", 1937).getId(); + final String demiM = saveActor(actors, "DemiM", "Demi Moore", 1962).getId(); + final String kevinB = saveActor(actors, "KevinB", "Kevin Bacon", 1958).getId(); + final String kieferS = saveActor(actors, "KieferS", "Kiefer Sutherland", 1966).getId(); + final String noahW = saveActor(actors, "NoahW", "Noah Wyle", 1971).getId(); + final String cubaG = saveActor(actors, "CubaG", "Cuba Gooding Jr.", 1968).getId(); + final String kevinP = saveActor(actors, "KevinP", "Kevin Pollak", 1957).getId(); + final String jTW = saveActor(actors, "JTW", "J.T. Walsh", 1943).getId(); + final String jamesM = saveActor(actors, "JamesM", "James Marshall", 1967).getId(); + final String christopherG = saveActor(actors, "ChristopherG", "Christopher Guest", 1948).getId(); + saveActsIn(actsIn, tomC, AFewGoodMen, new String[]{"Lt. Daniel Kaffee"}, 1992); + saveActsIn(actsIn, jackN, AFewGoodMen, new String[]{"Col. Nathan R. Jessup"}, 1992); + saveActsIn(actsIn, demiM, AFewGoodMen, new String[]{"Lt. Cdr. JoAnne Galloway"}, 1992); + saveActsIn(actsIn, kevinB, AFewGoodMen, new String[]{"Capt. Jack Ross"}, 1992); + saveActsIn(actsIn, kieferS, AFewGoodMen, new String[]{"Lt. Jonathan Kendrick"}, 1992); + saveActsIn(actsIn, noahW, AFewGoodMen, new String[]{"Cpl. Jeffrey Barnes"}, 1992); + saveActsIn(actsIn, cubaG, AFewGoodMen, new String[]{"Cpl. Carl Hammaker"}, 1992); + saveActsIn(actsIn, kevinP, AFewGoodMen, new String[]{"Lt. Sam Weinberg"}, 1992); + saveActsIn(actsIn, jTW, AFewGoodMen, new String[]{"Lt. Col. Matthew Andrew Markinson"}, 1992); + saveActsIn(actsIn, jamesM, AFewGoodMen, new String[]{"Pfc. Louden Downey"}, 1992); + saveActsIn(actsIn, christopherG, AFewGoodMen, new String[]{"Dr. Stone"}, 1992); + + final String topGun = saveMovie(movies, "TopGun", "Top Gun", 1986, "I feel the need, the need for speed.") + .getId(); + final String kellyM = saveActor(actors, "KellyM", "Kelly McGillis", 1957).getId(); + final String valK = saveActor(actors, "ValK", "Val Kilmer", 1959).getId(); + final String anthonyE = saveActor(actors, "AnthonyE", "Anthony Edwards", 1962).getId(); + final String tomS = saveActor(actors, "TomS", "Tom Skerritt", 1933).getId(); + final String megR = saveActor(actors, "MegR", "Meg Ryan", 1961).getId(); + saveActsIn(actsIn, tomC, topGun, new String[]{"Maverick"}, 1986); + saveActsIn(actsIn, kellyM, topGun, new String[]{"Charlie"}, 1986); + saveActsIn(actsIn, valK, topGun, new String[]{"Iceman"}, 1986); + saveActsIn(actsIn, anthonyE, topGun, new String[]{"Goose"}, 1986); + saveActsIn(actsIn, tomS, topGun, new String[]{"Viper"}, 1986); + saveActsIn(actsIn, megR, topGun, new String[]{"Carole"}, 1986); + + final String jerryMaguire = saveMovie(movies, "JerryMaguire", "Jerry Maguire", 2000, + "The rest of his life begins now.").getId(); + final String reneeZ = saveActor(actors, "ReneeZ", "Renee Zellweger", 1969).getId(); + final String kellyP = saveActor(actors, "KellyP", "Kelly Preston", 1962).getId(); + final String jerryO = saveActor(actors, "JerryO", "Jerry O'Connell", 1974).getId(); + final String jayM = saveActor(actors, "JayM", "Jay Mohr", 1970).getId(); + final String bonnieH = saveActor(actors, "BonnieH", "Bonnie Hunt", 1961).getId(); + final String reginaK = saveActor(actors, "ReginaK", "Regina King", 1971).getId(); + final String jonathanL = saveActor(actors, "JonathanL", "Jonathan Lipnicki", 1996).getId(); + saveActsIn(actsIn, tomC, jerryMaguire, new String[]{"Jerry Maguire"}, 2000); + saveActsIn(actsIn, cubaG, jerryMaguire, new String[]{"Rod Tidwell"}, 2000); + saveActsIn(actsIn, reneeZ, jerryMaguire, new String[]{"Dorothy Boyd"}, 2000); + saveActsIn(actsIn, kellyP, jerryMaguire, new String[]{"Avery Bishop"}, 2000); + saveActsIn(actsIn, jerryO, jerryMaguire, new String[]{"Frank Cushman"}, 2000); + saveActsIn(actsIn, jayM, jerryMaguire, new String[]{"Bob Sugar"}, 2000); + saveActsIn(actsIn, bonnieH, jerryMaguire, new String[]{"Laurel Boyd"}, 2000); + saveActsIn(actsIn, reginaK, jerryMaguire, new String[]{"Marcee Tidwell"}, 2000); + saveActsIn(actsIn, jonathanL, jerryMaguire, new String[]{"Ray Boyd"}, 2000); + + final String standByMe = saveMovie(movies, "StandByMe", "Stand By Me", 1986, + "For some, it's the last real taste of innocence, and the first real taste of life. But for everyone," + + " it's the time that memories are made of.") + .getId(); + final String riverP = saveActor(actors, "RiverP", "River Phoenix", 1970).getId(); + final String coreyF = saveActor(actors, "CoreyF", "Corey Feldman", 1971).getId(); + final String wilW = saveActor(actors, "WilW", "Wil Wheaton", 1972).getId(); + final String johnC = saveActor(actors, "JohnC", "John Cusack", 1966).getId(); + final String marshallB = saveActor(actors, "MarshallB", "Marshall Bell", 1942).getId(); + saveActsIn(actsIn, wilW, standByMe, new String[]{"Gordie Lachance"}, 1986); + saveActsIn(actsIn, riverP, standByMe, new String[]{"Chris Chambers"}, 1986); + saveActsIn(actsIn, jerryO, standByMe, new String[]{"Vern Tessio"}, 1986); + saveActsIn(actsIn, coreyF, standByMe, new String[]{"Teddy Duchamp"}, 1986); + saveActsIn(actsIn, johnC, standByMe, new String[]{"Denny Lachance"}, 1986); + saveActsIn(actsIn, kieferS, standByMe, new String[]{"Ace Merrill"}, 1986); + saveActsIn(actsIn, marshallB, standByMe, new String[]{"Mr. Lachance"}, 1986); + + final String asGoodAsItGets = saveMovie(movies, "AsGoodAsItGets", "As Good as It Gets", 1997, + "A comedy from the heart that goes for the throat.").getId(); + final String helenH = saveActor(actors, "HelenH", "Helen Hunt", 1963).getId(); + final String gregK = saveActor(actors, "GregK", "Greg Kinnear", 1963).getId(); + saveActsIn(actsIn, jackN, asGoodAsItGets, new String[]{"Melvin Udall"}, 1997); + saveActsIn(actsIn, helenH, asGoodAsItGets, new String[]{"Carol Connelly"}, 1997); + saveActsIn(actsIn, gregK, asGoodAsItGets, new String[]{"Simon Bishop"}, 1997); + saveActsIn(actsIn, cubaG, asGoodAsItGets, new String[]{"Frank Sachs"}, 1997); + + final String whatDreamsMayCome = saveMovie(movies, "WhatDreamsMayCome", "What Dreams May Come", 1998, + "After life there is more. The end is just the beginning.").getId(); + final String annabellaS = saveActor(actors, "AnnabellaS", "Annabella Sciorra", 1960).getId(); + final String maxS = saveActor(actors, "MaxS", "Max von Sydow", 1929).getId(); + final String wernerH = saveActor(actors, "WernerH", "Werner Herzog", 1942).getId(); + final String robin = saveActor(actors, "Robin", "Robin Williams", 1951).getId(); + saveActsIn(actsIn, robin, whatDreamsMayCome, new String[]{"Chris Nielsen"}, 1998); + saveActsIn(actsIn, cubaG, whatDreamsMayCome, new String[]{"Albert Lewis"}, 1998); + saveActsIn(actsIn, annabellaS, whatDreamsMayCome, new String[]{"Annie Collins-Nielsen"}, 1998); + saveActsIn(actsIn, maxS, whatDreamsMayCome, new String[]{"The Tracker"}, 1998); + saveActsIn(actsIn, wernerH, whatDreamsMayCome, new String[]{"The Face"}, 1998); + + final String snowFallingonCedars = saveMovie(movies, "SnowFallingonCedars", "Snow Falling on Cedars", 1999, + "First loves last. Forever.").getId(); + final String ethanH = saveActor(actors, "EthanH", "Ethan Hawke", 1970).getId(); + final String rickY = saveActor(actors, "RickY", "Rick Yune", 1971).getId(); + final String jamesC = saveActor(actors, "JamesC", "James Cromwell", 1940).getId(); + saveActsIn(actsIn, ethanH, snowFallingonCedars, new String[]{"Ishmael Chambers"}, 1999); + saveActsIn(actsIn, rickY, snowFallingonCedars, new String[]{"Kazuo Miyamoto"}, 1999); + saveActsIn(actsIn, maxS, snowFallingonCedars, new String[]{"Nels Gudmundsson"}, 1999); + saveActsIn(actsIn, jamesC, snowFallingonCedars, new String[]{"Judge Fielding"}, 1999); + + final String youveGotMail = saveMovie(movies, "YouveGotMail", "You've Got Mail", 1998, + "At odds in life... in love on-line.").getId(); + final String parkerP = saveActor(actors, "ParkerP", "Parker Posey", 1968).getId(); + final String daveC = saveActor(actors, "DaveC", "Dave Chappelle", 1973).getId(); + final String steveZ = saveActor(actors, "SteveZ", "Steve Zahn", 1967).getId(); + final String tomH = saveActor(actors, "TomH", "Tom Hanks", 1956).getId(); + saveActsIn(actsIn, tomH, youveGotMail, new String[]{"Joe Fox"}, 1998); + saveActsIn(actsIn, megR, youveGotMail, new String[]{"Kathleen Kelly"}, 1998); + saveActsIn(actsIn, gregK, youveGotMail, new String[]{"Frank Navasky"}, 1998); + saveActsIn(actsIn, parkerP, youveGotMail, new String[]{"Patricia Eden"}, 1998); + saveActsIn(actsIn, daveC, youveGotMail, new String[]{"Kevin Jackson"}, 1998); + saveActsIn(actsIn, steveZ, youveGotMail, new String[]{"George Pappas"}, 1998); + + final String sleeplessInSeattle = saveMovie(movies, "SleeplessInSeattle", "Sleepless in Seattle", 1993, + "What if someone you never met, someone you never saw, someone you never knew was the only someone " + + "for you?") + .getId(); + final String ritaW = saveActor(actors, "RitaW", "Rita Wilson", 1956).getId(); + final String billPull = saveActor(actors, "BillPull", "Bill Pullman", 1953).getId(); + final String victorG = saveActor(actors, "VictorG", "Victor Garber", 1949).getId(); + final String rosieO = saveActor(actors, "RosieO", "Rosie O'Donnell", 1962).getId(); + saveActsIn(actsIn, tomH, sleeplessInSeattle, new String[]{"Sam Baldwin"}, 1993); + saveActsIn(actsIn, megR, sleeplessInSeattle, new String[]{"Annie Reed"}, 1993); + saveActsIn(actsIn, ritaW, sleeplessInSeattle, new String[]{"Suzy"}, 1993); + saveActsIn(actsIn, billPull, sleeplessInSeattle, new String[]{"Walter"}, 1993); + saveActsIn(actsIn, victorG, sleeplessInSeattle, new String[]{"Greg"}, 1993); + saveActsIn(actsIn, rosieO, sleeplessInSeattle, new String[]{"Becky"}, 1993); + + final String joeVersustheVolcano = saveMovie(movies, "JoeVersustheVolcano", "Joe Versus the Volcano", 1990, + "A story of love, lava and burning desire.").getId(); + final String nathan = saveActor(actors, "Nathan", "Nathan Lane", 1956).getId(); + saveActsIn(actsIn, tomH, joeVersustheVolcano, new String[]{"Joe Banks"}, 1990); + saveActsIn(actsIn, megR, joeVersustheVolcano, + new String[]{"DeDe', 'Angelica Graynamore', 'Patricia Graynamore"}, 1990); + saveActsIn(actsIn, nathan, joeVersustheVolcano, new String[]{"Baw"}, 1990); + + final String whenHarryMetSally = saveMovie(movies, "WhenHarryMetSally", "When Harry Met Sally", 1998, + "At odds in life... in love on-line.").getId(); + final String billyC = saveActor(actors, "BillyC", "Billy Crystal", 1948).getId(); + final String carrieF = saveActor(actors, "CarrieF", "Carrie Fisher", 1956).getId(); + final String brunoK = saveActor(actors, "BrunoK", "Bruno Kirby", 1949).getId(); + saveActsIn(actsIn, billyC, whenHarryMetSally, new String[]{"Harry Burns"}, 1998); + saveActsIn(actsIn, megR, whenHarryMetSally, new String[]{"Sally Albright"}, 1998); + saveActsIn(actsIn, carrieF, whenHarryMetSally, new String[]{"Marie"}, 1998); + saveActsIn(actsIn, brunoK, whenHarryMetSally, new String[]{"Jess"}, 1998); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allActorsActsInMovie1or2() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "WITH actors, movies FOR x IN ANY 'movies/TheMatrix' actsIn OPTIONS {bfs: true, uniqueVertices: " + + "'global'} RETURN x._id", + null, null, String.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains("actors/Keanu", "actors/Hugo", + "actors/Emil", "actors/Carrie", "actors/Laurence")).get(); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allActorsActsInMovie1or2UnionDistinct() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "WITH actors, movies FOR x IN UNION_DISTINCT ((FOR y IN ANY 'movies/TheMatrix' actsIn OPTIONS {bfs: " + + "true, uniqueVertices: 'global'} RETURN y._id), (FOR y IN ANY 'movies/TheDevilsAdvocate' " + + "actsIn OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN y._id)) RETURN x", + null, null, String.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains( + "actors/Emil", "actors/Hugo", "actors/Carrie", + "actors/Laurence", "actors/Keanu", "actors/Al", "actors/Charlize")).get(); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allActorsActsInMovie1and2() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "WITH actors, movies FOR x IN INTERSECTION ((FOR y IN ANY 'movies/TheMatrix' actsIn OPTIONS {bfs: " + + "true, uniqueVertices: 'global'} RETURN y._id), (FOR y IN ANY 'movies/TheDevilsAdvocate' " + + "actsIn OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN y._id)) RETURN x", + null, null, String.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains("actors/Keanu")).get(); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allMoviesBetweenActor1andActor2() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "WITH actors, movies FOR x IN INTERSECTION ((FOR y IN ANY 'actors/Hugo' actsIn OPTIONS {bfs: true, " + + "uniqueVertices: 'global'} RETURN y._id), (FOR y IN ANY 'actors/Keanu' actsIn OPTIONS {bfs: " + + "true, uniqueVertices: 'global'} RETURN y._id)) RETURN x", + null, null, String.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains("movies/TheMatrixRevolutions", + "movies/TheMatrixReloaded", "movies/TheMatrix")).get(); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allActorsWhoActedIn3orMoreMovies() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "FOR x IN actsIn COLLECT actor = x._from WITH COUNT INTO counter FILTER counter >= 3 RETURN {actor: " + + "actor, movies: counter}", + null, null, Actor.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains( + new Actor("actors/Carrie", 3), new Actor("actors/CubaG", 4), new Actor("actors/Hugo", 3), + new Actor("actors/Keanu", 4), new Actor("actors/Laurence", 3), new Actor("actors/MegR", 5), + new Actor("actors/TomC", 3), new Actor("actors/TomH", 3))).get(); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allMoviesWhereExactly6ActorsActedIn() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "FOR x IN actsIn COLLECT movie = x._to WITH COUNT INTO counter FILTER counter == 6 RETURN movie", null, + null, String.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains("movies/SleeplessInSeattle", + "movies/TopGun", "movies/YouveGotMail")).get(); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void theNumberOfActorsByMovie() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "FOR x IN actsIn COLLECT movie = x._to WITH COUNT INTO counter RETURN {movie: movie, actors: counter}", + null, null, Movie.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains( + new Movie("movies/AFewGoodMen", 11), new Movie("movies/AsGoodAsItGets", 4), + new Movie("movies/JerryMaguire", 9), new Movie("movies/JoeVersustheVolcano", 3), + new Movie("movies/SleeplessInSeattle", 6), new Movie("movies/SnowFallingonCedars", 4), + new Movie("movies/StandByMe", 7), new Movie("movies/TheDevilsAdvocate", 3), + new Movie("movies/TheMatrix", 5), new Movie("movies/TheMatrixReloaded", 4), + new Movie("movies/TheMatrixRevolutions", 4), new Movie("movies/TopGun", 6), + new Movie("movies/WhatDreamsMayCome", 5), new Movie("movies/WhenHarryMetSally", 4), + new Movie("movies/YouveGotMail", 6))).get(); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void theNumberOfMoviesByActor() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "FOR x IN actsIn COLLECT actor = x._from WITH COUNT INTO counter RETURN {actor: actor, movies: " + + "counter}", + null, null, Actor.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains( + new Actor("actors/Al", 1), new Actor("actors/AnnabellaS", 1), new Actor("actors/AnthonyE", 1), + new Actor("actors/BillPull", 1), new Actor("actors/BillyC", 1), new Actor("actors/BonnieH", 1), + new Actor("actors/BrunoK", 1), new Actor("actors/Carrie", 3), new Actor("actors/CarrieF", 1), + new Actor("actors/Charlize", 1), new Actor("actors/ChristopherG", 1), new Actor("actors/CoreyF", 1), + new Actor("actors/CubaG", 4), new Actor("actors/DaveC", 1), new Actor("actors/DemiM", 1), + new Actor("actors/Emil", 1), new Actor("actors/EthanH", 1), new Actor("actors/GregK", 2), + new Actor("actors/HelenH", 1), new Actor("actors/Hugo", 3), new Actor("actors/JackN", 2), + new Actor("actors/JamesC", 1), new Actor("actors/JamesM", 1), new Actor("actors/JayM", 1), + new Actor("actors/JerryO", 2), new Actor("actors/JohnC", 1), new Actor("actors/JonathanL", 1), + new Actor("actors/JTW", 1), new Actor("actors/Keanu", 4), new Actor("actors/KellyM", 1), + new Actor("actors/KellyP", 1), new Actor("actors/KevinB", 1), new Actor("actors/KevinP", 1), + new Actor("actors/KieferS", 2), new Actor("actors/Laurence", 3), new Actor("actors/MarshallB", 1), + new Actor("actors/MaxS", 2), new Actor("actors/MegR", 5), new Actor("actors/Nathan", 1), + new Actor("actors/NoahW", 1), new Actor("actors/ParkerP", 1), new Actor("actors/ReginaK", 1), + new Actor("actors/ReneeZ", 1), new Actor("actors/RickY", 1), new Actor("actors/RitaW", 1), + new Actor("actors/RiverP", 1), new Actor("actors/Robin", 1), new Actor("actors/RosieO", 1), + new Actor("actors/SteveZ", 1), new Actor("actors/TomC", 3), new Actor("actors/TomH", 3), + new Actor("actors/TomS", 1), new Actor("actors/ValK", 1), new Actor("actors/VictorG", 1), + new Actor("actors/WernerH", 1), new Actor("actors/WilW", 1))).get(); + } + + /** + * @throws ExecutionException + * @throws InterruptedException + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void theNumberOfMoviesActedInBetween2005and2010byActor() throws InterruptedException, ExecutionException { + final CompletableFuture> f = db.query( + "FOR x IN actsIn FILTER x.year >= 1990 && x.year <= 1995 COLLECT actor = x._from WITH COUNT INTO " + + "counter RETURN {actor: actor, movies: counter}", + null, null, Actor.class); + f.whenComplete((cursor, ex) -> assertThat(cursor.asListRemaining()).contains( + new Actor("actors/BillPull", 1), new Actor("actors/ChristopherG", 1), + new Actor("actors/CubaG", 1), new Actor("actors/DemiM", 1), new Actor("actors/JackN", 1), + new Actor("actors/JamesM", 1), new Actor("actors/JTW", 1), new Actor("actors/KevinB", 1), + new Actor("actors/KieferS", 1), new Actor("actors/MegR", 2), new Actor("actors/Nathan", 1), + new Actor("actors/NoahW", 1), new Actor("actors/RitaW", 1), new Actor("actors/RosieO", 1), + new Actor("actors/TomC", 1), new Actor("actors/TomH", 2), new Actor("actors/VictorG", 1))).get(); + } + + public static class Actor { + private final String actor; + private final Integer movies; + + + @JsonCreator + public Actor(@JsonProperty("actor") final String actor, @JsonProperty("movies") final Integer movies) { + super(); + this.actor = actor; + this.movies = movies; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((actor == null) ? 0 : actor.hashCode()); + result = prime * result + ((movies == null) ? 0 : movies.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Actor other = (Actor) obj; + if (actor == null) { + if (other.actor != null) { + return false; + } + } else if (!actor.equals(other.actor)) { + return false; + } + if (movies == null) { + return other.movies == null; + } else return movies.equals(other.movies); + } + + } + + public static class Movie { + private final String movie; + private final Integer actors; + + @JsonCreator + public Movie(@JsonProperty("movie") final String movie, @JsonProperty("actors") final Integer actors) { + super(); + this.movie = movie; + this.actors = actors; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((actors == null) ? 0 : actors.hashCode()); + result = prime * result + ((movie == null) ? 0 : movie.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Movie other = (Movie) obj; + if (actors == null) { + if (other.actors != null) { + return false; + } + } else if (!actors.equals(other.actors)) { + return false; + } + if (movie == null) { + return other.movie == null; + } else return movie.equals(other.movie); + } + + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/BaseGraphTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/BaseGraphTest.java new file mode 100644 index 000000000..8a16b9231 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/BaseGraphTest.java @@ -0,0 +1,116 @@ +/* + * 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.async.example.graph; + +import com.arangodb.DbName; +import com.arangodb.async.ArangoDBAsync; +import com.arangodb.async.ArangoDatabaseAsync; +import com.arangodb.entity.EdgeDefinition; +import com.arangodb.entity.VertexEntity; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ExecutionException; + +/** + * @author Mark Vollmary + */ +public abstract class BaseGraphTest { + + private static final DbName TEST_DB = DbName.of("java_driver_graph_test_db"); + private static final String GRAPH_NAME = "traversalGraph"; + private static final String EDGE_COLLECTION_NAME = "edges"; + private static final String VERTEX_COLLECTION_NAME = "circles"; + static ArangoDatabaseAsync db; + private static ArangoDBAsync arangoDB; + + @BeforeAll + static void init() throws InterruptedException, ExecutionException { + if (arangoDB == null) { + arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + } + if (arangoDB.db(TEST_DB).exists().get()) { + arangoDB.db(TEST_DB).drop().get(); + } + arangoDB.createDatabase(TEST_DB).get(); + BaseGraphTest.db = arangoDB.db(TEST_DB); + + final Collection edgeDefinitions = new ArrayList<>(); + final EdgeDefinition edgeDefinition = new EdgeDefinition().collection(EDGE_COLLECTION_NAME) + .from(VERTEX_COLLECTION_NAME).to(VERTEX_COLLECTION_NAME); + edgeDefinitions.add(edgeDefinition); + db.createGraph(GRAPH_NAME, edgeDefinitions, null).get(); + addExampleElements(); + } + + @AfterAll + static void shutdown() throws InterruptedException, ExecutionException { + arangoDB.db(TEST_DB).drop().get(); + arangoDB.shutdown(); + arangoDB = null; + } + + private static void addExampleElements() throws InterruptedException, ExecutionException { + + // Add circle circles + final VertexEntity vA = createVertex(new Circle("A", "1")); + final VertexEntity vB = createVertex(new Circle("B", "2")); + final VertexEntity vC = createVertex(new Circle("C", "3")); + final VertexEntity vD = createVertex(new Circle("D", "4")); + final VertexEntity vE = createVertex(new Circle("E", "5")); + final VertexEntity vF = createVertex(new Circle("F", "6")); + final VertexEntity vG = createVertex(new Circle("G", "7")); + final VertexEntity vH = createVertex(new Circle("H", "8")); + final VertexEntity vI = createVertex(new Circle("I", "9")); + final VertexEntity vJ = createVertex(new Circle("J", "10")); + final VertexEntity vK = createVertex(new Circle("K", "11")); + + // Add relevant edges - left branch: + saveEdge(new CircleEdge(vA.getId(), vB.getId(), false, true, "left_bar")); + saveEdge(new CircleEdge(vB.getId(), vC.getId(), false, true, "left_blarg")); + saveEdge(new CircleEdge(vC.getId(), vD.getId(), false, true, "left_blorg")); + saveEdge(new CircleEdge(vB.getId(), vE.getId(), false, true, "left_blub")); + saveEdge(new CircleEdge(vE.getId(), vF.getId(), false, true, "left_schubi")); + + // Add relevant edges - right branch: + saveEdge(new CircleEdge(vA.getId(), vG.getId(), false, true, "right_foo")); + saveEdge(new CircleEdge(vG.getId(), vH.getId(), false, true, "right_blob")); + saveEdge(new CircleEdge(vH.getId(), vI.getId(), false, true, "right_blub")); + saveEdge(new CircleEdge(vG.getId(), vJ.getId(), false, true, "right_zip")); + saveEdge(new CircleEdge(vJ.getId(), vK.getId(), false, true, "right_zup")); + } + + private static void saveEdge(final CircleEdge edge) + throws InterruptedException, ExecutionException { + db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(edge).get(); + } + + private static VertexEntity createVertex(final Circle vertex) + throws InterruptedException, ExecutionException { + return db.graph(GRAPH_NAME).vertexCollection(VERTEX_COLLECTION_NAME).insertVertex(vertex).get(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/Circle.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/Circle.java new file mode 100644 index 000000000..e828aaeb3 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/Circle.java @@ -0,0 +1,80 @@ +/* + * 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.async.example.graph; + +import com.arangodb.serde.jackson.Id; +import com.arangodb.serde.jackson.Key; +import com.arangodb.serde.jackson.Rev; + +/** + * @author a-brandt + */ +class Circle { + + @Id + private String id; + + @Key + private String key; + + @Rev + private String revision; + + private String label; + + public Circle(String key, String label) { + this.key = key; + this.label = label; + } + + public String getId() { + return id; + } + + void setId(String id) { + this.id = id; + } + + public String getKey() { + return key; + } + + void setKey(String key) { + this.key = key; + } + + public String getRevision() { + return revision; + } + + void setRevision(String revision) { + this.revision = revision; + } + + public String getLabel() { + return label; + } + + void setLabel(String label) { + this.label = label; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/CircleEdge.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/CircleEdge.java new file mode 100644 index 000000000..bfaa65210 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/CircleEdge.java @@ -0,0 +1,122 @@ +/* + * 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.async.example.graph; + +import com.arangodb.serde.jackson.*; + +/** + * @author a-brandt + */ +class CircleEdge { + + @Id + private String id; + + @Key + private String key; + + @Rev + private String revision; + + @From + private String from; + + @To + private String to; + + private Boolean theFalse; + private Boolean theTruth; + private String label; + + public CircleEdge(final String from, final String to, final Boolean theFalse, final Boolean theTruth, + final String label) { + this.from = from; + this.to = to; + this.theFalse = theFalse; + this.theTruth = theTruth; + this.label = label; + } + + public String getId() { + return id; + } + + void setId(String id) { + this.id = id; + } + + public String getKey() { + return key; + } + + void setKey(String key) { + this.key = key; + } + + public String getRevision() { + return revision; + } + + void setRevision(String revision) { + this.revision = revision; + } + + public String getFrom() { + return from; + } + + void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + void setTo(String to) { + this.to = to; + } + + public Boolean getTheFalse() { + return theFalse; + } + + void setTheFalse(Boolean theFalse) { + this.theFalse = theFalse; + } + + public Boolean getTheTruth() { + return theTruth; + } + + void setTheTruth(Boolean theTruth) { + this.theTruth = theTruth; + } + + public String getLabel() { + return label; + } + + void setLabel(String label) { + this.label = label; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/GraphTraversalsInAQLExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/GraphTraversalsInAQLExampleTest.java new file mode 100644 index 000000000..b3628f572 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/GraphTraversalsInAQLExampleTest.java @@ -0,0 +1,122 @@ +/* + * 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.async.example.graph; + +import com.arangodb.async.ArangoCursorAsync; +import org.junit.jupiter.api.Test; + +import java.util.Collection; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * Graph traversals in AQL + * + * @author a-brandt + * @see Graph traversals in AQL + */ +class GraphTraversalsInAQLExampleTest extends BaseGraphTest { + + @Test + void queryAllVertices() throws InterruptedException, ExecutionException { + String queryString = "FOR v IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' RETURN v._key"; + ArangoCursorAsync cursor = db.query(queryString, null, null, String.class).get(); + Collection result = cursor.asListRemaining(); + assertThat(result).hasSize(10); + + queryString = "WITH circles FOR v IN 1..3 OUTBOUND 'circles/A' edges RETURN v._key"; + cursor = db.query(queryString, null, null, String.class).get(); + result = cursor.asListRemaining(); + assertThat(result).hasSize(10); + } + + @Test + void queryDepthTwo() throws InterruptedException, ExecutionException { + String queryString = "FOR v IN 2..2 OUTBOUND 'circles/A' GRAPH 'traversalGraph' return v._key"; + ArangoCursorAsync cursor = db.query(queryString, null, null, String.class).get(); + Collection result = cursor.asListRemaining(); + assertThat(result).hasSize(4); + assertThat(result).contains("C", "E", "H", "J"); + + queryString = "FOR v IN 2 OUTBOUND 'circles/A' GRAPH 'traversalGraph' return v._key"; + cursor = db.query(queryString, null, null, String.class).get(); + result = cursor.asListRemaining(); + assertThat(result).hasSize(4); + assertThat(result).contains("C", "E", "H", "J"); + } + + @Test + void queryWithFilter() throws InterruptedException, ExecutionException { + String queryString = "FOR v, e, p IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' FILTER p.vertices[1]" + + "._key != 'G' RETURN v._key"; + ArangoCursorAsync cursor = db.query(queryString, null, null, String.class).get(); + Collection result = cursor.asListRemaining(); + assertThat(result).hasSize(5); + assertThat(result).contains("B", "C", "D", "E", "F"); + + queryString = "FOR v, e, p IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' FILTER p.edges[0].label != " + + "'right_foo' RETURN v._key"; + cursor = db.query(queryString, null, null, String.class).get(); + result = cursor.asListRemaining(); + assertThat(result).hasSize(5); + assertThat(result).contains("B", "C", "D", "E", "F"); + + queryString = "FOR v,e,p IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' FILTER p.vertices[1]._key != 'G'" + + " FILTER p.edges[1].label != 'left_blub' return v._key"; + cursor = db.query(queryString, null, null, String.class).get(); + + result = cursor.asListRemaining(); + assertThat(result).hasSize(3); + assertThat(result).contains("B", "C", "D"); + + queryString = "FOR v,e,p IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' FILTER p.vertices[1]._key != 'G'" + + " AND p.edges[1].label != 'left_blub' return v._key"; + cursor = db.query(queryString, null, null, String.class).get(); + result = cursor.asListRemaining(); + assertThat(result).hasSize(3); + assertThat(result).contains("B", "C", "D"); + } + + @Test + void queryOutboundInbound() throws InterruptedException, ExecutionException { + String queryString = "FOR v IN 1..3 OUTBOUND 'circles/E' GRAPH 'traversalGraph' return v._key"; + ArangoCursorAsync cursor = db.query(queryString, null, null, String.class).get(); + Collection result = cursor.asListRemaining(); + assertThat(result).hasSize(1); + assertThat(result).contains("F"); + + queryString = "FOR v IN 1..3 INBOUND 'circles/E' GRAPH 'traversalGraph' return v._key"; + cursor = db.query(queryString, null, null, String.class).get(); + result = cursor.asListRemaining(); + assertThat(result).hasSize(2); + assertThat(result).contains("B", "A"); + + queryString = "FOR v IN 1..3 ANY 'circles/E' GRAPH 'traversalGraph' return v._key"; + cursor = db.query(queryString, null, null, String.class).get(); + + result = cursor.asListRemaining(); + assertThat(result).hasSize(6); + assertThat(result).contains("F", "B", "C", "D", "A", "G"); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/ShortestPathInAQLExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/ShortestPathInAQLExampleTest.java new file mode 100644 index 000000000..515f8e13f --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/graph/ShortestPathInAQLExampleTest.java @@ -0,0 +1,106 @@ +/* + * 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.async.example.graph; + +import com.arangodb.async.ArangoCursorAsync; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * Shortest Path in AQL + * + * @author a-brandt + * @see Shortest Path in AQL + */ +class ShortestPathInAQLExampleTest extends BaseGraphTest { + + @Test + void queryShortestPathFromAToD() throws InterruptedException, ExecutionException { + String queryString = "FOR v, e IN OUTBOUND SHORTEST_PATH 'circles/A' TO 'circles/D' GRAPH 'traversalGraph' " + + "RETURN {'vertex': v._key, 'edge': e._key}"; + ArangoCursorAsync cursor = db.query(queryString, null, null, Pair.class).get(); + final Collection collection = toVertexCollection(cursor); + assertThat(collection).hasSize(4); + assertThat(collection).contains("A", "B", "C", "D"); + + queryString = "WITH circles FOR v, e IN OUTBOUND SHORTEST_PATH 'circles/A' TO 'circles/D' edges RETURN " + + "{'vertex': v._key, 'edge': e._key}"; + db.query(queryString, null, null, Pair.class).get(); + assertThat(collection).hasSize(4); + assertThat(collection).contains("A", "B", "C", "D"); + } + + @Test + void queryShortestPathByFilter() throws InterruptedException, ExecutionException { + String queryString = "FOR a IN circles FILTER a._key == 'A' FOR d IN circles FILTER d._key == 'D' FOR v, e IN" + + " OUTBOUND SHORTEST_PATH a TO d GRAPH 'traversalGraph' RETURN {'vertex':v._key, 'edge':e._key}"; + ArangoCursorAsync cursor = db.query(queryString, null, null, Pair.class).get(); + final Collection collection = toVertexCollection(cursor); + assertThat(collection).hasSize(4); + assertThat(collection).contains("A", "B", "C", "D"); + + queryString = "FOR a IN circles FILTER a._key == 'A' FOR d IN circles FILTER d._key == 'D' FOR v, e IN " + + "OUTBOUND SHORTEST_PATH a TO d edges RETURN {'vertex': v._key, 'edge': e._key}"; + db.query(queryString, null, null, Pair.class).get(); + assertThat(collection).hasSize(4); + assertThat(collection).contains("A", "B", "C", "D"); + } + + private Collection toVertexCollection(final ArangoCursorAsync cursor) { + final List result = new ArrayList<>(); + while (cursor.hasNext()) { + final Pair pair = cursor.next(); + result.add(pair.getVertex()); + } + return result; + } + + public static class Pair { + + private String vertex; + private String edge; + + public String getVertex() { + return vertex; + } + + void setVertex(final String vertex) { + this.vertex = vertex; + } + + public String getEdge() { + return edge; + } + + void setEdge(final String edge) { + this.edge = edge; + } + + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/ssl/SslExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/ssl/SslExampleTest.java new file mode 100644 index 000000000..c90175310 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/example/ssl/SslExampleTest.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 com.arangodb.async.example.ssl; + +import com.arangodb.async.ArangoDBAsync; +import com.arangodb.entity.ArangoDBVersion; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import java.security.KeyStore; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +@Tag("ssl") +@EnabledIfSystemProperty(named = "SslTest", matches = "true") +class SslExampleTest { + + /*- + * a SSL trust store + * + * create the trust store for the self signed certificate: + * keytool -import -alias "my arangodb server cert" -file UnitTests/server.pem -keystore example.truststore + * + * Documentation: + * https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLSocketFactory.html + */ + private static final String SSL_TRUSTSTORE = "/example.truststore"; + private static final String SSL_TRUSTSTORE_PASSWORD = "12345678"; + + @Test + void connect() throws Exception { + final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(this.getClass().getResourceAsStream(SSL_TRUSTSTORE), SSL_TRUSTSTORE_PASSWORD.toCharArray()); + + final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, SSL_TRUSTSTORE_PASSWORD.toCharArray()); + + final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + + final SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + final ArangoDBAsync arangoDB = new ArangoDBAsync.Builder() + .host("localhost", 8529) + .password("test") + .useSsl(true) + .sslContext(sc).build(); + final ArangoDBVersion version = arangoDB.getVersion().get(); + assertThat(version).isNotNull(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/serde/CustomSerdeTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/serde/CustomSerdeTest.java new file mode 100644 index 000000000..c6e65ca50 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/async/serde/CustomSerdeTest.java @@ -0,0 +1,177 @@ +/* + * 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.async.serde; + + +import com.arangodb.ContentType; +import com.arangodb.DbName; +import com.arangodb.async.ArangoCollectionAsync; +import com.arangodb.async.ArangoDBAsync; +import com.arangodb.async.ArangoDatabaseAsync; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import com.arangodb.model.DocumentCreateOptions; +import com.arangodb.serde.jackson.JacksonSerde; +import com.arangodb.serde.jackson.JacksonSerdeProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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 CustomSerdeTest { + + private static final String COLLECTION_NAME = "collection"; + + private ArangoDatabaseAsync db; + private ArangoCollectionAsync collection; + + @BeforeEach + void init() throws ExecutionException, InterruptedException { + JacksonSerde serde = new JacksonSerdeProvider().of(ContentType.VPACK); + serde.configure((mapper) -> { + mapper.configure(WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true); + mapper.configure(USE_BIG_INTEGER_FOR_INTS, true); + }); + ArangoDBAsync arangoDB = new ArangoDBAsync.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .serializer(serde).build(); + + DbName TEST_DB = DbName.of("custom-serde-test"); + db = arangoDB.db(TEST_DB); + if (!db.exists().get()) { + db.create().get(); + } + + collection = db.collection(COLLECTION_NAME); + if (!collection.exists().get()) { + collection.create().get(); + } + } + + @AfterEach + void shutdown() throws ExecutionException, InterruptedException { + db.drop().get(); + } + + @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", + params, + Map.class + ).get().next(); + + 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).get(); + + final Map result = db.query( + "RETURN DOCUMENT(@docId)", + Collections.singletonMap("docId", COLLECTION_NAME + "/" + key), + Map.class + ).get().next(); + + 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)); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/ExampleBase.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/ExampleBase.java new file mode 100644 index 000000000..c99610836 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/ExampleBase.java @@ -0,0 +1,62 @@ +/* + * 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.example; + +import com.arangodb.ArangoCollection; +import com.arangodb.ArangoDB; +import com.arangodb.ArangoDatabase; +import com.arangodb.DbName; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +/** + * @author Mark Vollmary + */ +public class ExampleBase { + + protected static final String COLLECTION_NAME = "json_example_collection"; + private static final String DB_NAME = "json_example_db"; + protected static ArangoDatabase db; + protected static ArangoCollection collection; + private static ArangoDB arangoDB; + + @BeforeAll + static void setUp() { + arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + DbName dbName = DbName.of(DB_NAME); + if (arangoDB.db(dbName).exists()) + arangoDB.db(dbName).drop(); + arangoDB.createDatabase(dbName); + db = arangoDB.db(dbName); + db.createCollection(COLLECTION_NAME); + collection = db.collection(COLLECTION_NAME); + } + + @AfterAll + static void tearDown() { + db.drop(); + arangoDB.shutdown(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/FirstProject.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/FirstProject.java new file mode 100644 index 000000000..454bfabc7 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/FirstProject.java @@ -0,0 +1,134 @@ +package com.arangodb.example; + +import com.arangodb.*; +import com.arangodb.entity.BaseDocument; +import com.arangodb.entity.CollectionEntity; +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.Collections; +import java.util.Map; +import java.util.UUID; + +public class FirstProject { + + public static void main(final String[] args) { + final ArangoDB arangoDB = new ArangoDB.Builder().user("root").build(); + + // create database + final DbName dbName = DbName.of("mydb"); + try { + arangoDB.createDatabase(dbName); + System.out.println("Database created: " + dbName); + } catch (final ArangoDBException e) { + System.err.println("Failed to create database: " + dbName + "; " + e.getMessage()); + } + + // create collection + final String collectionName = "firstCollection"; + try { + final CollectionEntity myArangoCollection = arangoDB.db(dbName).createCollection(collectionName); + System.out.println("Collection created: " + myArangoCollection.getName()); + } catch (final ArangoDBException e) { + System.err.println("Failed to create collection: " + collectionName + "; " + e.getMessage()); + } + + // creating a document + final BaseDocument myObject = new BaseDocument(UUID.randomUUID().toString()); + myObject.setKey("myKey"); + myObject.addAttribute("a", "Foo"); + myObject.addAttribute("b", 42); + try { + arangoDB.db(dbName).collection(collectionName).insertDocument(myObject); + System.out.println("Document created"); + } catch (final ArangoDBException e) { + System.err.println("Failed to create document. " + e.getMessage()); + } + + // read a document + try { + final BaseDocument myDocument = arangoDB.db(dbName).collection(collectionName).getDocument("myKey", + BaseDocument.class); + System.out.println("Key: " + myDocument.getKey()); + System.out.println("Attribute a: " + myDocument.getAttribute("a")); + System.out.println("Attribute b: " + myDocument.getAttribute("b")); + } catch (final ArangoDBException e) { + System.err.println("Failed to get document: myKey; " + e.getMessage()); + } + + // read a document as JsonNode + try { + final JsonNode myDocument = arangoDB.db(dbName).collection(collectionName).getDocument("myKey", + JsonNode.class); + System.out.println("Key: " + myDocument.get("_key").textValue()); + System.out.println("Attribute a: " + myDocument.get("a").textValue()); + System.out.println("Attribute b: " + myDocument.get("b").textValue()); + } catch (final ArangoDBException e) { + System.err.println("Failed to get document: myKey; " + e.getMessage()); + } + + // update a document + myObject.addAttribute("c", "Bar"); + try { + arangoDB.db(dbName).collection(collectionName).updateDocument("myKey", myObject); + } catch (final ArangoDBException e) { + System.err.println("Failed to update document. " + e.getMessage()); + } + + // read the document again + try { + final BaseDocument myUpdatedDocument = arangoDB.db(dbName).collection(collectionName).getDocument("myKey", + BaseDocument.class); + System.out.println("Key: " + myUpdatedDocument.getKey()); + System.out.println("Attribute a: " + myUpdatedDocument.getAttribute("a")); + System.out.println("Attribute b: " + myUpdatedDocument.getAttribute("b")); + System.out.println("Attribute c: " + myUpdatedDocument.getAttribute("c")); + } catch (final ArangoDBException e) { + System.err.println("Failed to get document: myKey; " + e.getMessage()); + } + + // delete a document + try { + arangoDB.db(dbName).collection(collectionName).deleteDocument("myKey"); + } catch (final ArangoDBException e) { + System.err.println("Failed to delete document. " + e.getMessage()); + } + + // create some documents for the next step + final ArangoCollection collection = arangoDB.db(dbName).collection(collectionName); + for (int i = 0; i < 10; i++) { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.setKey(String.valueOf(i)); + value.addAttribute("name", "Homer"); + collection.insertDocument(value); + } + + // execute AQL queries + try { + final String query = "FOR t IN firstCollection FILTER t.name == @name RETURN t"; + final Map bindVars = Collections.singletonMap("name", "Homer"); + final ArangoCursor cursor = arangoDB.db(dbName).query(query, bindVars, null, + BaseDocument.class); + while (cursor.hasNext()) { + System.out.println("Key: " + cursor.next().getKey()); + } + } catch (final ArangoDBException e) { + System.err.println("Failed to execute query. " + e.getMessage()); + } + + // delete a document with AQL + try { + final String query = "FOR t IN firstCollection FILTER t.name == @name " + + "REMOVE t IN firstCollection LET removed = OLD RETURN removed"; + final Map bindVars = Collections.singletonMap("name", "Homer"); + final ArangoCursor cursor = arangoDB.db(dbName).query(query, bindVars, null, + BaseDocument.class); + while (cursor.hasNext()) { + System.out.println("Removed document " + cursor.next().getKey()); + } + } catch (final ArangoDBException e) { + System.err.println("Failed to execute query. " + e.getMessage()); + } + + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/AqlQueryWithSpecialReturnTypesExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/AqlQueryWithSpecialReturnTypesExampleTest.java new file mode 100644 index 000000000..9c9279c28 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/AqlQueryWithSpecialReturnTypesExampleTest.java @@ -0,0 +1,132 @@ +/* + * 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.example.document; + +import com.arangodb.ArangoCursor; +import com.arangodb.entity.BaseDocument; +import com.arangodb.example.ExampleBase; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +class AqlQueryWithSpecialReturnTypesExampleTest extends ExampleBase { + + @BeforeAll + static void before() { + createExamples(); + } + + private static void createExamples() { + for (int i = 0; i < 100; i++) { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.addAttribute("name", "TestUser" + i); + value.addAttribute("gender", (i % 2) == 0 ? Gender.MALE : Gender.FEMALE); + value.addAttribute("age", i + 10); + db.collection(COLLECTION_NAME).insertDocument(value); + } + } + + @Test + void aqlWithLimitQueryAsJsonObject() { + final String query = "FOR t IN " + COLLECTION_NAME + + " FILTER t.age >= 20 && t.age < 30 && t.gender == @gender RETURN t"; + final Map bindVars = Collections.singletonMap("gender", Gender.FEMALE); + final ArangoCursor cursor = db.query(query, bindVars, null, ObjectNode.class); + assertThat((Object) cursor).isNotNull(); + while (cursor.hasNext()) { + final ObjectNode vpack = cursor.next(); + assertThat(vpack.get("name").asText()) + .isIn("TestUser11", "TestUser13", "TestUser15", "TestUser17", "TestUser19"); + assertThat(vpack.get("gender").asText()).isEqualTo(Gender.FEMALE.name()); + assertThat(vpack.get("age").asInt()).isIn(21, 23, 25, 27, 29); + } + } + + @Test + void aqlWithLimitQueryAsJsonArray() { + final String query = "FOR t IN " + COLLECTION_NAME + + " FILTER t.age >= 20 && t.age < 30 && t.gender == @gender RETURN [t.name, t.gender, t.age]"; + final Map bindVars = Collections.singletonMap("gender", Gender.FEMALE); + final ArangoCursor cursor = db.query(query, bindVars, null, ArrayNode.class); + assertThat((Object) cursor).isNotNull(); + while (cursor.hasNext()) { + final ArrayNode arrNode = cursor.next(); + assertThat(arrNode.get(0).asText()) + .isIn("TestUser11", "TestUser13", "TestUser15", "TestUser17", "TestUser19"); + assertThat(arrNode.get(1).asText()).isEqualTo(Gender.FEMALE.name()); + assertThat(arrNode.get(2).asInt()).isIn(21, 23, 25, 27, 29); + } + } + + @Test + void aqlWithLimitQueryAsMap() { + final String query = "FOR t IN " + COLLECTION_NAME + + " FILTER t.age >= 20 && t.age < 30 && t.gender == @gender RETURN t"; + final Map bindVars = Collections.singletonMap("gender", Gender.FEMALE); + final ArangoCursor cursor = db.query(query, bindVars, null, Map.class); + assertThat((Object) cursor).isNotNull(); + while (cursor.hasNext()) { + final Map map = cursor.next(); + assertThat(map.get("name")).isNotNull(); + assertThat(String.valueOf(map.get("name"))) + .isIn("TestUser11", "TestUser13", "TestUser15", "TestUser17", "TestUser19"); + assertThat(map.get("gender")).isNotNull(); + assertThat(String.valueOf(map.get("gender"))).isEqualTo(Gender.FEMALE.name()); + assertThat(map.get("age")).isNotNull(); + assertThat(Long.valueOf(map.get("age").toString())).isIn(21L, 23L, 25L, 27L, 29L); + } + } + + @Test + void aqlWithLimitQueryAsList() { + final String query = "FOR t IN " + COLLECTION_NAME + + " FILTER t.age >= 20 && t.age < 30 && t.gender == @gender RETURN [t.name, t.gender, t.age]"; + final Map bindVars = Collections.singletonMap("gender", Gender.FEMALE); + final ArangoCursor cursor = db.query(query, bindVars, null, List.class); + assertThat((Object) cursor).isNotNull(); + while (cursor.hasNext()) { + final List list = cursor.next(); + assertThat(list.get(0)).isNotNull(); + assertThat(String.valueOf(list.get(0))) + .isIn("TestUser11", "TestUser13", "TestUser15", "TestUser17", "TestUser19"); + assertThat(list.get(1)).isNotNull(); + assertThat(Gender.valueOf(String.valueOf(list.get(1)))).isEqualTo(Gender.FEMALE); + assertThat(list.get(2)).isNotNull(); + assertThat(Long.valueOf(String.valueOf(list.get(2)))).isIn(21L, 23L, 25L, 27L, 29L); + } + } + + enum Gender { + MALE, FEMALE + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/GetDocumentExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/GetDocumentExampleTest.java new file mode 100644 index 000000000..dc7318139 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/GetDocumentExampleTest.java @@ -0,0 +1,98 @@ +/* + * 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.example.document; + +import com.arangodb.entity.BaseDocument; +import com.arangodb.example.ExampleBase; +import com.arangodb.util.RawBytes; +import com.arangodb.util.RawJson; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.Map; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +class GetDocumentExampleTest extends ExampleBase { + + private static String key = null; + + @BeforeAll + static void before() { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.addAttribute("foo", "bar"); + key = collection.insertDocument(value).getKey(); + } + + @Test + void getAsBean() { + final TestEntity doc = collection.getDocument(key, TestEntity.class); + assertThat(doc).isNotNull(); + assertThat(doc.getFoo()).isEqualTo("bar"); + } + + @Test + void getAsBaseDocument() { + final BaseDocument doc = collection.getDocument(key, BaseDocument.class); + assertThat(doc).isNotNull(); + assertThat(doc.getAttribute("foo")).isNotNull(); + assertThat(String.valueOf(doc.getAttribute("foo"))).isEqualTo("bar"); + } + + @Test + void getAsMap() { + final Map doc = collection.getDocument(key, Map.class); + assertThat(doc).isNotNull(); + assertThat(doc.get("foo")).isNotNull(); + assertThat(String.valueOf(doc.get("foo"))).isEqualTo("bar"); + } + + @Test + void getAsJsonNode() { + final JsonNode doc = collection.getDocument(key, JsonNode.class); + assertThat(doc).isNotNull(); + assertThat(doc.get("foo").isTextual()).isTrue(); + assertThat(doc.get("foo").asText()).isEqualTo("bar"); + } + + @Test + void getAsJson() { + final RawJson doc = collection.getDocument(key, RawJson.class); + assertThat(doc.getValue()).isNotNull() + .contains("foo") + .contains("bar"); + } + + @Test + void getAsBytes() { + final RawBytes doc = collection.getDocument(key, RawBytes.class); + assertThat(doc.getValue()).isNotNull(); + Map mapDoc = collection.getSerde().deserializeUserData(doc.getValue(), Map.class); + assertThat(mapDoc).containsEntry("foo", "bar"); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/InsertDocumentExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/InsertDocumentExampleTest.java new file mode 100644 index 000000000..000b6ddd6 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/InsertDocumentExampleTest.java @@ -0,0 +1,70 @@ +/* + * 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.example.document; + +import com.arangodb.entity.BaseDocument; +import com.arangodb.entity.DocumentCreateEntity; +import com.arangodb.example.ExampleBase; +import com.arangodb.util.RawJson; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +class InsertDocumentExampleTest extends ExampleBase { + + @Test + void insertBean() { + final DocumentCreateEntity doc = collection.insertDocument(new TestEntity("bar")); + assertThat(doc.getKey()).isNotNull(); + } + + @Test + void insertBaseDocument() { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.addAttribute("foo", "bar"); + final DocumentCreateEntity doc = collection.insertDocument(value); + assertThat(doc.getKey()).isNotNull(); + } + + @Test + void insertJsonNode() { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode node = mapper.createObjectNode(); + node.put("foo", "bar"); + final DocumentCreateEntity doc = collection.insertDocument(node); + assertThat(doc.getKey()).isNotNull(); + } + + @Test + void insertJson() { + final DocumentCreateEntity doc = collection.insertDocument(RawJson.of("{\"foo\":\"bar\"}")); + assertThat(doc.getKey()).isNotNull(); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/TestEntity.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/TestEntity.java new file mode 100644 index 000000000..8a59c2b60 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/document/TestEntity.java @@ -0,0 +1,47 @@ +/* + * 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.example.document; + +/** + * @author Mark Vollmary + */ +public class TestEntity { + + private String foo; + + public TestEntity() { + super(); + } + + public TestEntity(final String foo) { + super(); + this.foo = foo; + } + + public String getFoo() { + return foo; + } + + public void setFoo(final String foo) { + this.foo = foo; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/AQLActorsAndMoviesExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/AQLActorsAndMoviesExampleTest.java new file mode 100644 index 000000000..682bed1b1 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/AQLActorsAndMoviesExampleTest.java @@ -0,0 +1,581 @@ +/* + * 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.example.graph; + +import com.arangodb.*; +import com.arangodb.entity.BaseDocument; +import com.arangodb.entity.BaseEdgeDocument; +import com.arangodb.entity.CollectionType; +import com.arangodb.entity.DocumentCreateEntity; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import com.arangodb.model.CollectionCreateOptions; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + * @see + * AQL Example Queries on an + * Actors and Movies Database + */ +class AQLActorsAndMoviesExampleTest { + + private static final DbName TEST_DB = DbName.of("actors_movies_test_db"); + private static ArangoDB arangoDB; + private static ArangoDatabase db; + + @BeforeAll + static void setUp() { + arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + if (arangoDB.db(TEST_DB).exists()) + arangoDB.db(TEST_DB).drop(); + arangoDB.createDatabase(TEST_DB); + db = arangoDB.db(TEST_DB); + createData(); + } + + @AfterAll + static void tearDown() { + db.drop(); + arangoDB.shutdown(); + } + + private static DocumentCreateEntity saveMovie( + final ArangoCollection movies, + final String key, + final String title, + final int released, + final String tagline) { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.setKey(key); + value.addAttribute("title", title); + value.addAttribute("released", released); + value.addAttribute("tagline", tagline); + return movies.insertDocument(value); + } + + private static DocumentCreateEntity saveActor( + final ArangoCollection actors, + final String key, + final String name, + final int born) { + final BaseDocument value = new BaseDocument(UUID.randomUUID().toString()); + value.setKey(key); + value.addAttribute("name", name); + value.addAttribute("born", born); + return actors.insertDocument(value); + } + + private static void saveActsIn( + final ArangoCollection actsIn, + final String actor, + final String movie, + final String[] roles, + final int year) { + final BaseEdgeDocument value = new BaseEdgeDocument(); + value.setFrom(actor); + value.setTo(movie); + value.addAttribute("roles", roles); + value.addAttribute("year", year); + actsIn.insertDocument(value); + } + + private static void createData() { + db.createCollection("actors"); + final ArangoCollection actors = db.collection("actors"); + db.createCollection("movies"); + final ArangoCollection movies = db.collection("movies"); + db.createCollection("actsIn", new CollectionCreateOptions().type(CollectionType.EDGES)); + final ArangoCollection actsIn = db.collection("actsIn"); + + final String theMatrix = saveMovie(movies, "TheMatrix", "The Matrix", 1999, "Welcome to the Real World") + .getId(); + final String keanu = saveActor(actors, "Keanu", "Keanu Reeves", 1964).getId(); + final String carrie = saveActor(actors, "Carrie", "Carrie-Anne Moss", 1967).getId(); + final String laurence = saveActor(actors, "Laurence", "Laurence Fishburne", 1961).getId(); + final String hugo = saveActor(actors, "Hugo", "Hugo Weaving", 1960).getId(); + final String emil = saveActor(actors, "Emil", "Emil Eifrem", 1978).getId(); + + saveActsIn(actsIn, keanu, theMatrix, new String[]{"Neo"}, 1999); + saveActsIn(actsIn, carrie, theMatrix, new String[]{"Trinity"}, 1999); + saveActsIn(actsIn, laurence, theMatrix, new String[]{"Morpheus"}, 1999); + saveActsIn(actsIn, hugo, theMatrix, new String[]{"Agent Smith"}, 1999); + saveActsIn(actsIn, emil, theMatrix, new String[]{"Emil"}, 1999); + + final String theMatrixReloaded = saveMovie(movies, "TheMatrixReloaded", "The Matrix Reloaded", 2003, + "Free your mind").getId(); + saveActsIn(actsIn, keanu, theMatrixReloaded, new String[]{"Neo"}, 2003); + saveActsIn(actsIn, carrie, theMatrixReloaded, new String[]{"Trinity"}, 2003); + saveActsIn(actsIn, laurence, theMatrixReloaded, new String[]{"Morpheus"}, 2003); + saveActsIn(actsIn, hugo, theMatrixReloaded, new String[]{"Agent Smith"}, 2003); + + final String theMatrixRevolutions = saveMovie(movies, "TheMatrixRevolutions", "The Matrix Revolutions", 2003, + "Everything that has a beginning has an end").getId(); + saveActsIn(actsIn, keanu, theMatrixRevolutions, new String[]{"Neo"}, 2003); + saveActsIn(actsIn, carrie, theMatrixRevolutions, new String[]{"Trinity"}, 2003); + saveActsIn(actsIn, laurence, theMatrixRevolutions, new String[]{"Morpheus"}, 2003); + saveActsIn(actsIn, hugo, theMatrixRevolutions, new String[]{"Agent Smith"}, 2003); + + final String theDevilsAdvocate = saveMovie(movies, "TheDevilsAdvocate", "The Devil's Advocate", 1997, + "Evil has its winning ways").getId(); + final String charlize = saveActor(actors, "Charlize", "Charlize Theron", 1975).getId(); + final String al = saveActor(actors, "Al", "Al Pacino", 1940).getId(); + saveActsIn(actsIn, keanu, theDevilsAdvocate, new String[]{"Kevin Lomax"}, 1997); + saveActsIn(actsIn, charlize, theDevilsAdvocate, new String[]{"Mary Ann Lomax"}, 1997); + saveActsIn(actsIn, al, theDevilsAdvocate, new String[]{"John Milton"}, 1997); + + final String AFewGoodMen = saveMovie(movies, "AFewGoodMen", "A Few Good Men", 1992, + "In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at " + + "nothing to keep his honor, and one will stop at nothing to find the truth.") + .getId(); + final String tomC = saveActor(actors, "TomC", "Tom Cruise", 1962).getId(); + final String jackN = saveActor(actors, "JackN", "Jack Nicholson", 1937).getId(); + final String demiM = saveActor(actors, "DemiM", "Demi Moore", 1962).getId(); + final String kevinB = saveActor(actors, "KevinB", "Kevin Bacon", 1958).getId(); + final String kieferS = saveActor(actors, "KieferS", "Kiefer Sutherland", 1966).getId(); + final String noahW = saveActor(actors, "NoahW", "Noah Wyle", 1971).getId(); + final String cubaG = saveActor(actors, "CubaG", "Cuba Gooding Jr.", 1968).getId(); + final String kevinP = saveActor(actors, "KevinP", "Kevin Pollak", 1957).getId(); + final String jTW = saveActor(actors, "JTW", "J.T. Walsh", 1943).getId(); + final String jamesM = saveActor(actors, "JamesM", "James Marshall", 1967).getId(); + final String christopherG = saveActor(actors, "ChristopherG", "Christopher Guest", 1948).getId(); + saveActsIn(actsIn, tomC, AFewGoodMen, new String[]{"Lt. Daniel Kaffee"}, 1992); + saveActsIn(actsIn, jackN, AFewGoodMen, new String[]{"Col. Nathan R. Jessup"}, 1992); + saveActsIn(actsIn, demiM, AFewGoodMen, new String[]{"Lt. Cdr. JoAnne Galloway"}, 1992); + saveActsIn(actsIn, kevinB, AFewGoodMen, new String[]{"Capt. Jack Ross"}, 1992); + saveActsIn(actsIn, kieferS, AFewGoodMen, new String[]{"Lt. Jonathan Kendrick"}, 1992); + saveActsIn(actsIn, noahW, AFewGoodMen, new String[]{"Cpl. Jeffrey Barnes"}, 1992); + saveActsIn(actsIn, cubaG, AFewGoodMen, new String[]{"Cpl. Carl Hammaker"}, 1992); + saveActsIn(actsIn, kevinP, AFewGoodMen, new String[]{"Lt. Sam Weinberg"}, 1992); + saveActsIn(actsIn, jTW, AFewGoodMen, new String[]{"Lt. Col. Matthew Andrew Markinson"}, 1992); + saveActsIn(actsIn, jamesM, AFewGoodMen, new String[]{"Pfc. Louden Downey"}, 1992); + saveActsIn(actsIn, christopherG, AFewGoodMen, new String[]{"Dr. Stone"}, 1992); + + final String topGun = saveMovie(movies, "TopGun", "Top Gun", 1986, "I feel the need, the need for speed.") + .getId(); + final String kellyM = saveActor(actors, "KellyM", "Kelly McGillis", 1957).getId(); + final String valK = saveActor(actors, "ValK", "Val Kilmer", 1959).getId(); + final String anthonyE = saveActor(actors, "AnthonyE", "Anthony Edwards", 1962).getId(); + final String tomS = saveActor(actors, "TomS", "Tom Skerritt", 1933).getId(); + final String megR = saveActor(actors, "MegR", "Meg Ryan", 1961).getId(); + saveActsIn(actsIn, tomC, topGun, new String[]{"Maverick"}, 1986); + saveActsIn(actsIn, kellyM, topGun, new String[]{"Charlie"}, 1986); + saveActsIn(actsIn, valK, topGun, new String[]{"Iceman"}, 1986); + saveActsIn(actsIn, anthonyE, topGun, new String[]{"Goose"}, 1986); + saveActsIn(actsIn, tomS, topGun, new String[]{"Viper"}, 1986); + saveActsIn(actsIn, megR, topGun, new String[]{"Carole"}, 1986); + + final String jerryMaguire = saveMovie(movies, "JerryMaguire", "Jerry Maguire", 2000, + "The rest of his life begins now.").getId(); + final String reneeZ = saveActor(actors, "ReneeZ", "Renee Zellweger", 1969).getId(); + final String kellyP = saveActor(actors, "KellyP", "Kelly Preston", 1962).getId(); + final String jerryO = saveActor(actors, "JerryO", "Jerry O'Connell", 1974).getId(); + final String jayM = saveActor(actors, "JayM", "Jay Mohr", 1970).getId(); + final String bonnieH = saveActor(actors, "BonnieH", "Bonnie Hunt", 1961).getId(); + final String reginaK = saveActor(actors, "ReginaK", "Regina King", 1971).getId(); + final String jonathanL = saveActor(actors, "JonathanL", "Jonathan Lipnicki", 1996).getId(); + saveActsIn(actsIn, tomC, jerryMaguire, new String[]{"Jerry Maguire"}, 2000); + saveActsIn(actsIn, cubaG, jerryMaguire, new String[]{"Rod Tidwell"}, 2000); + saveActsIn(actsIn, reneeZ, jerryMaguire, new String[]{"Dorothy Boyd"}, 2000); + saveActsIn(actsIn, kellyP, jerryMaguire, new String[]{"Avery Bishop"}, 2000); + saveActsIn(actsIn, jerryO, jerryMaguire, new String[]{"Frank Cushman"}, 2000); + saveActsIn(actsIn, jayM, jerryMaguire, new String[]{"Bob Sugar"}, 2000); + saveActsIn(actsIn, bonnieH, jerryMaguire, new String[]{"Laurel Boyd"}, 2000); + saveActsIn(actsIn, reginaK, jerryMaguire, new String[]{"Marcee Tidwell"}, 2000); + saveActsIn(actsIn, jonathanL, jerryMaguire, new String[]{"Ray Boyd"}, 2000); + + final String standByMe = saveMovie(movies, "StandByMe", "Stand By Me", 1986, + "For some, it's the last real taste of innocence, and the first real taste of life. But for everyone," + + " it's the time that memories are made of.") + .getId(); + final String riverP = saveActor(actors, "RiverP", "River Phoenix", 1970).getId(); + final String coreyF = saveActor(actors, "CoreyF", "Corey Feldman", 1971).getId(); + final String wilW = saveActor(actors, "WilW", "Wil Wheaton", 1972).getId(); + final String johnC = saveActor(actors, "JohnC", "John Cusack", 1966).getId(); + final String marshallB = saveActor(actors, "MarshallB", "Marshall Bell", 1942).getId(); + saveActsIn(actsIn, wilW, standByMe, new String[]{"Gordie Lachance"}, 1986); + saveActsIn(actsIn, riverP, standByMe, new String[]{"Chris Chambers"}, 1986); + saveActsIn(actsIn, jerryO, standByMe, new String[]{"Vern Tessio"}, 1986); + saveActsIn(actsIn, coreyF, standByMe, new String[]{"Teddy Duchamp"}, 1986); + saveActsIn(actsIn, johnC, standByMe, new String[]{"Denny Lachance"}, 1986); + saveActsIn(actsIn, kieferS, standByMe, new String[]{"Ace Merrill"}, 1986); + saveActsIn(actsIn, marshallB, standByMe, new String[]{"Mr. Lachance"}, 1986); + + final String asGoodAsItGets = saveMovie(movies, "AsGoodAsItGets", "As Good as It Gets", 1997, + "A comedy from the heart that goes for the throat.").getId(); + final String helenH = saveActor(actors, "HelenH", "Helen Hunt", 1963).getId(); + final String gregK = saveActor(actors, "GregK", "Greg Kinnear", 1963).getId(); + saveActsIn(actsIn, jackN, asGoodAsItGets, new String[]{"Melvin Udall"}, 1997); + saveActsIn(actsIn, helenH, asGoodAsItGets, new String[]{"Carol Connelly"}, 1997); + saveActsIn(actsIn, gregK, asGoodAsItGets, new String[]{"Simon Bishop"}, 1997); + saveActsIn(actsIn, cubaG, asGoodAsItGets, new String[]{"Frank Sachs"}, 1997); + + final String whatDreamsMayCome = saveMovie(movies, "WhatDreamsMayCome", "What Dreams May Come", 1998, + "After life there is more. The end is just the beginning.").getId(); + final String annabellaS = saveActor(actors, "AnnabellaS", "Annabella Sciorra", 1960).getId(); + final String maxS = saveActor(actors, "MaxS", "Max von Sydow", 1929).getId(); + final String wernerH = saveActor(actors, "WernerH", "Werner Herzog", 1942).getId(); + final String robin = saveActor(actors, "Robin", "Robin Williams", 1951).getId(); + saveActsIn(actsIn, robin, whatDreamsMayCome, new String[]{"Chris Nielsen"}, 1998); + saveActsIn(actsIn, cubaG, whatDreamsMayCome, new String[]{"Albert Lewis"}, 1998); + saveActsIn(actsIn, annabellaS, whatDreamsMayCome, new String[]{"Annie Collins-Nielsen"}, 1998); + saveActsIn(actsIn, maxS, whatDreamsMayCome, new String[]{"The Tracker"}, 1998); + saveActsIn(actsIn, wernerH, whatDreamsMayCome, new String[]{"The Face"}, 1998); + + final String snowFallingonCedars = saveMovie(movies, "SnowFallingonCedars", "Snow Falling on Cedars", 1999, + "First loves last. Forever.").getId(); + final String ethanH = saveActor(actors, "EthanH", "Ethan Hawke", 1970).getId(); + final String rickY = saveActor(actors, "RickY", "Rick Yune", 1971).getId(); + final String jamesC = saveActor(actors, "JamesC", "James Cromwell", 1940).getId(); + saveActsIn(actsIn, ethanH, snowFallingonCedars, new String[]{"Ishmael Chambers"}, 1999); + saveActsIn(actsIn, rickY, snowFallingonCedars, new String[]{"Kazuo Miyamoto"}, 1999); + saveActsIn(actsIn, maxS, snowFallingonCedars, new String[]{"Nels Gudmundsson"}, 1999); + saveActsIn(actsIn, jamesC, snowFallingonCedars, new String[]{"Judge Fielding"}, 1999); + + final String youveGotMail = saveMovie(movies, "YouveGotMail", "You've Got Mail", 1998, + "At odds in life... in love on-line.").getId(); + final String parkerP = saveActor(actors, "ParkerP", "Parker Posey", 1968).getId(); + final String daveC = saveActor(actors, "DaveC", "Dave Chappelle", 1973).getId(); + final String steveZ = saveActor(actors, "SteveZ", "Steve Zahn", 1967).getId(); + final String tomH = saveActor(actors, "TomH", "Tom Hanks", 1956).getId(); + saveActsIn(actsIn, tomH, youveGotMail, new String[]{"Joe Fox"}, 1998); + saveActsIn(actsIn, megR, youveGotMail, new String[]{"Kathleen Kelly"}, 1998); + saveActsIn(actsIn, gregK, youveGotMail, new String[]{"Frank Navasky"}, 1998); + saveActsIn(actsIn, parkerP, youveGotMail, new String[]{"Patricia Eden"}, 1998); + saveActsIn(actsIn, daveC, youveGotMail, new String[]{"Kevin Jackson"}, 1998); + saveActsIn(actsIn, steveZ, youveGotMail, new String[]{"George Pappas"}, 1998); + + final String sleeplessInSeattle = saveMovie(movies, "SleeplessInSeattle", "Sleepless in Seattle", 1993, + "What if someone you never met, someone you never saw, someone you never knew was the only someone " + + "for you?") + .getId(); + final String ritaW = saveActor(actors, "RitaW", "Rita Wilson", 1956).getId(); + final String billPull = saveActor(actors, "BillPull", "Bill Pullman", 1953).getId(); + final String victorG = saveActor(actors, "VictorG", "Victor Garber", 1949).getId(); + final String rosieO = saveActor(actors, "RosieO", "Rosie O'Donnell", 1962).getId(); + saveActsIn(actsIn, tomH, sleeplessInSeattle, new String[]{"Sam Baldwin"}, 1993); + saveActsIn(actsIn, megR, sleeplessInSeattle, new String[]{"Annie Reed"}, 1993); + saveActsIn(actsIn, ritaW, sleeplessInSeattle, new String[]{"Suzy"}, 1993); + saveActsIn(actsIn, billPull, sleeplessInSeattle, new String[]{"Walter"}, 1993); + saveActsIn(actsIn, victorG, sleeplessInSeattle, new String[]{"Greg"}, 1993); + saveActsIn(actsIn, rosieO, sleeplessInSeattle, new String[]{"Becky"}, 1993); + + final String joeVersustheVolcano = saveMovie(movies, "JoeVersustheVolcano", "Joe Versus the Volcano", 1990, + "A story of love, lava and burning desire.").getId(); + final String nathan = saveActor(actors, "Nathan", "Nathan Lane", 1956).getId(); + saveActsIn(actsIn, tomH, joeVersustheVolcano, new String[]{"Joe Banks"}, 1990); + saveActsIn(actsIn, megR, joeVersustheVolcano, + new String[]{"DeDe', 'Angelica Graynamore', 'Patricia Graynamore"}, 1990); + saveActsIn(actsIn, nathan, joeVersustheVolcano, new String[]{"Baw"}, 1990); + + final String whenHarryMetSally = saveMovie(movies, "WhenHarryMetSally", "When Harry Met Sally", 1998, + "At odds in life... in love on-line.").getId(); + final String billyC = saveActor(actors, "BillyC", "Billy Crystal", 1948).getId(); + final String carrieF = saveActor(actors, "CarrieF", "Carrie Fisher", 1956).getId(); + final String brunoK = saveActor(actors, "BrunoK", "Bruno Kirby", 1949).getId(); + saveActsIn(actsIn, billyC, whenHarryMetSally, new String[]{"Harry Burns"}, 1998); + saveActsIn(actsIn, megR, whenHarryMetSally, new String[]{"Sally Albright"}, 1998); + saveActsIn(actsIn, carrieF, whenHarryMetSally, new String[]{"Marie"}, 1998); + saveActsIn(actsIn, brunoK, whenHarryMetSally, new String[]{"Jess"}, 1998); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allActorsActsInMovie1or2() { + final ArangoCursor cursor = db.query( + "WITH actors, movies FOR x IN ANY 'movies/TheMatrix' actsIn OPTIONS {bfs: true, uniqueVertices: " + + "'global'} RETURN x._id", + null, null, String.class); + assertThat(cursor.asListRemaining()) + .contains("actors/Keanu", "actors/Hugo", "actors/Emil", "actors/Carrie", "actors/Laurence"); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allActorsActsInMovie1or2UnionDistinct() { + final ArangoCursor cursor = db.query( + "WITH actors, movies FOR x IN UNION_DISTINCT ((FOR y IN ANY 'movies/TheMatrix' actsIn OPTIONS {bfs: " + + "true, uniqueVertices: 'global'} RETURN y._id), (FOR y IN ANY 'movies/TheDevilsAdvocate' " + + "actsIn OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN y._id)) RETURN x", + null, null, String.class); + assertThat(cursor.asListRemaining()).contains("actors/Emil", "actors/Hugo", "actors/Carrie", "actors/Laurence", + "actors/Keanu", "actors/Al", "actors/Charlize"); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allActorsActsInMovie1and2() { + final ArangoCursor cursor = db.query( + "WITH actors, movies FOR x IN INTERSECTION ((FOR y IN ANY 'movies/TheMatrix' actsIn OPTIONS {bfs: " + + "true, uniqueVertices: 'global'} RETURN y._id), (FOR y IN ANY 'movies/TheDevilsAdvocate' " + + "actsIn OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN y._id)) RETURN x", + null, null, String.class); + assertThat(cursor.asListRemaining()).contains("actors/Keanu"); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allMoviesBetweenActor1andActor2() { + final ArangoCursor cursor = db.query( + "WITH actors, movies FOR x IN INTERSECTION ((FOR y IN ANY 'actors/Hugo' actsIn OPTIONS {bfs: true, " + + "uniqueVertices: 'global'} RETURN y._id), (FOR y IN ANY 'actors/Keanu' actsIn OPTIONS {bfs: " + + "true, uniqueVertices: 'global'} RETURN y._id)) RETURN x", + null, null, String.class); + assertThat(cursor.asListRemaining()) + .contains("movies/TheMatrixRevolutions", "movies/TheMatrixReloaded", "movies/TheMatrix"); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allActorsWhoActedIn3orMoreMovies() { + final ArangoCursor cursor = db.query( + "FOR x IN actsIn COLLECT actor = x._from WITH COUNT INTO counter FILTER counter >= 3 RETURN {actor: " + + "actor, movies: counter}", + null, null, Actor.class); + assertThat(cursor.asListRemaining()) + .contains(new Actor("actors/Carrie", 3), new Actor("actors/CubaG", 4), new Actor("actors/Hugo", 3), + new Actor("actors/Keanu", 4), new Actor("actors/Laurence", 3), new Actor("actors/MegR", 5), + new Actor("actors/TomC", 3), new Actor("actors/TomH", 3)); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void allMoviesWhereExactly6ActorsActedIn() { + final ArangoCursor cursor = db.query( + "FOR x IN actsIn COLLECT movie = x._to WITH COUNT INTO counter FILTER counter == 6 RETURN movie", null, + null, String.class); + assertThat(cursor.asListRemaining()) + .contains("movies/SleeplessInSeattle", "movies/TopGun", "movies/YouveGotMail"); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void theNumberOfActorsByMovie() { + final ArangoCursor cursor = db.query( + "FOR x IN actsIn COLLECT movie = x._to WITH COUNT INTO counter RETURN {movie: movie, actors: counter}", + null, null, Movie.class); + assertThat(cursor.asListRemaining()) + .contains(new Movie("movies/AFewGoodMen", 11), new Movie("movies/AsGoodAsItGets", 4), + new Movie("movies/JerryMaguire", 9), new Movie("movies/JoeVersustheVolcano", 3), + new Movie("movies/SleeplessInSeattle", 6), new Movie("movies/SnowFallingonCedars", 4), + new Movie("movies/StandByMe", 7), new Movie("movies/TheDevilsAdvocate", 3), + new Movie("movies/TheMatrix", 5), new Movie("movies/TheMatrixReloaded", 4), + new Movie("movies/TheMatrixRevolutions", 4), new Movie("movies/TopGun", 6), + new Movie("movies/WhatDreamsMayCome", 5), new Movie("movies/WhenHarryMetSally", 4), + new Movie("movies/YouveGotMail", 6)); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void theNumberOfMoviesByActor() { + final ArangoCursor cursor = db.query( + "FOR x IN actsIn COLLECT actor = x._from WITH COUNT INTO counter RETURN {actor: actor, movies: " + + "counter}", + null, null, Actor.class); + assertThat(cursor.asListRemaining()) + .contains(new Actor("actors/Al", 1), new Actor("actors/AnnabellaS", 1), new Actor("actors/AnthonyE", 1), + new Actor("actors/BillPull", 1), new Actor("actors/BillyC", 1), new Actor("actors/BonnieH", 1), + new Actor("actors/BrunoK", 1), new Actor("actors/Carrie", 3), new Actor("actors/CarrieF", 1), + new Actor("actors/Charlize", 1), new Actor("actors/ChristopherG", 1), new Actor("actors" + + "/CoreyF", 1), + new Actor("actors/CubaG", 4), new Actor("actors/DaveC", 1), new Actor("actors/DemiM", 1), + new Actor("actors/Emil", 1), new Actor("actors/EthanH", 1), new Actor("actors/GregK", 2), + new Actor("actors/HelenH", 1), new Actor("actors/Hugo", 3), new Actor("actors/JackN", 2), + new Actor("actors/JamesC", 1), new Actor("actors/JamesM", 1), new Actor("actors/JayM", 1), + new Actor("actors/JerryO", 2), new Actor("actors/JohnC", 1), new Actor("actors/JonathanL", 1), + new Actor("actors/JTW", 1), new Actor("actors/Keanu", 4), new Actor("actors/KellyM", 1), + new Actor("actors/KellyP", 1), new Actor("actors/KevinB", 1), new Actor("actors/KevinP", 1), + new Actor("actors/KieferS", 2), new Actor("actors/Laurence", 3), new Actor("actors/MarshallB" + , 1), + new Actor("actors/MaxS", 2), new Actor("actors/MegR", 5), new Actor("actors/Nathan", 1), + new Actor("actors/NoahW", 1), new Actor("actors/ParkerP", 1), new Actor("actors/ReginaK", 1), + new Actor("actors/ReneeZ", 1), new Actor("actors/RickY", 1), new Actor("actors/RitaW", 1), + new Actor("actors/RiverP", 1), new Actor("actors/Robin", 1), new Actor("actors/RosieO", 1), + new Actor("actors/SteveZ", 1), new Actor("actors/TomC", 3), new Actor("actors/TomH", 3), + new Actor("actors/TomS", 1), new Actor("actors/ValK", 1), new Actor("actors/VictorG", 1), + new Actor("actors/WernerH", 1), new Actor("actors/WilW", 1)); + } + + /** + * @see AQL + * Example Queries on an Actors and Movies Database + */ + @Test + void theNumberOfMoviesActedInBetween2005and2010byActor() { + final ArangoCursor cursor = db.query( + "FOR x IN actsIn FILTER x.year >= 1990 && x.year <= 1995 COLLECT actor = x._from WITH COUNT INTO " + + "counter RETURN {actor: actor, movies: counter}", + null, null, Actor.class); + assertThat(cursor.asListRemaining()) + .contains(new Actor("actors/BillPull", 1), new Actor("actors/ChristopherG", 1), new Actor("actors" + + "/CubaG", 1), + new Actor("actors/DemiM", 1), new Actor("actors/JackN", 1), new Actor("actors/JamesM", 1), + new Actor("actors/JTW", 1), new Actor("actors/KevinB", 1), new Actor("actors/KieferS", 1), + new Actor("actors/MegR", 2), new Actor("actors/Nathan", 1), new Actor("actors/NoahW", 1), + new Actor("actors/RitaW", 1), new Actor("actors/RosieO", 1), new Actor("actors/TomC", 1), + new Actor("actors/TomH", 2), new Actor("actors/VictorG", 1)); + } + + public static class Actor { + private final String actor; + private final Integer movies; + + @JsonCreator + Actor(@JsonProperty("actor") final String actor, @JsonProperty("movies") final Integer movies) { + super(); + this.actor = actor; + this.movies = movies; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((actor == null) ? 0 : actor.hashCode()); + result = prime * result + ((movies == null) ? 0 : movies.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Actor other = (Actor) obj; + if (actor == null) { + if (other.actor != null) { + return false; + } + } else if (!actor.equals(other.actor)) { + return false; + } + if (movies == null) { + return other.movies == null; + } else return movies.equals(other.movies); + } + + } + + public static class Movie { + private final String movie; + private final Integer actors; + + @JsonCreator + public Movie(@JsonProperty("movie") final String movie, @JsonProperty("actors") final Integer actors) { + super(); + this.movie = movie; + this.actors = actors; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((actors == null) ? 0 : actors.hashCode()); + result = prime * result + ((movie == null) ? 0 : movie.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Movie other = (Movie) obj; + if (actors == null) { + if (other.actors != null) { + return false; + } + } else if (!actors.equals(other.actors)) { + return false; + } + if (movie == null) { + return other.movie == null; + } else return movie.equals(other.movie); + } + + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/BaseGraphTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/BaseGraphTest.java new file mode 100644 index 000000000..514af943f --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/BaseGraphTest.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.example.graph; + +import com.arangodb.ArangoDB; +import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDatabase; +import com.arangodb.DbName; +import com.arangodb.entity.EdgeDefinition; +import com.arangodb.entity.VertexEntity; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * @author Mark Vollmary + */ +abstract class BaseGraphTest { + + private static final DbName TEST_DB = DbName.of("java_driver_graph_test_db"); + private static final String GRAPH_NAME = "traversalGraph"; + private static final String EDGE_COLLECTION_NAME = "edges"; + private static final String VERTEX_COLLECTION_NAME = "circles"; + static ArangoDatabase db; + private static ArangoDB arangoDB; + + @BeforeAll + static void init() { + if (arangoDB == null) { + arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + } + if (arangoDB.db(TEST_DB).exists()) + arangoDB.db(TEST_DB).drop(); + arangoDB.createDatabase(TEST_DB); + BaseGraphTest.db = arangoDB.db(TEST_DB); + + final Collection edgeDefinitions = new ArrayList<>(); + final EdgeDefinition edgeDefinition = new EdgeDefinition().collection(EDGE_COLLECTION_NAME) + .from(VERTEX_COLLECTION_NAME).to(VERTEX_COLLECTION_NAME); + edgeDefinitions.add(edgeDefinition); + if (!db.graph(GRAPH_NAME).exists()) + db.createGraph(GRAPH_NAME, edgeDefinitions, null); + addExampleElements(); + } + + @AfterAll + static void shutdown() { + arangoDB.db(TEST_DB).drop(); + arangoDB.shutdown(); + arangoDB = null; + } + + private static void addExampleElements() throws ArangoDBException { + + // Add circle circles + final VertexEntity vA = createVertex(new Circle("A", "1")); + final VertexEntity vB = createVertex(new Circle("B", "2")); + final VertexEntity vC = createVertex(new Circle("C", "3")); + final VertexEntity vD = createVertex(new Circle("D", "4")); + final VertexEntity vE = createVertex(new Circle("E", "5")); + final VertexEntity vF = createVertex(new Circle("F", "6")); + final VertexEntity vG = createVertex(new Circle("G", "7")); + final VertexEntity vH = createVertex(new Circle("H", "8")); + final VertexEntity vI = createVertex(new Circle("I", "9")); + final VertexEntity vJ = createVertex(new Circle("J", "10")); + final VertexEntity vK = createVertex(new Circle("K", "11")); + + // Add relevant edges - left branch: + saveEdge(new CircleEdge(vA.getId(), vB.getId(), false, true, "left_bar")); + saveEdge(new CircleEdge(vB.getId(), vC.getId(), false, true, "left_blarg")); + saveEdge(new CircleEdge(vC.getId(), vD.getId(), false, true, "left_blorg")); + saveEdge(new CircleEdge(vB.getId(), vE.getId(), false, true, "left_blub")); + saveEdge(new CircleEdge(vE.getId(), vF.getId(), false, true, "left_schubi")); + + // Add relevant edges - right branch: + saveEdge(new CircleEdge(vA.getId(), vG.getId(), false, true, "right_foo")); + saveEdge(new CircleEdge(vG.getId(), vH.getId(), false, true, "right_blob")); + saveEdge(new CircleEdge(vH.getId(), vI.getId(), false, true, "right_blub")); + saveEdge(new CircleEdge(vG.getId(), vJ.getId(), false, true, "right_zip")); + saveEdge(new CircleEdge(vJ.getId(), vK.getId(), false, true, "right_zup")); + } + + private static void saveEdge(final CircleEdge edge) throws ArangoDBException { + db.graph(GRAPH_NAME).edgeCollection(EDGE_COLLECTION_NAME).insertEdge(edge); + } + + private static VertexEntity createVertex(final Circle vertex) throws ArangoDBException { + return db.graph(GRAPH_NAME).vertexCollection(VERTEX_COLLECTION_NAME).insertVertex(vertex); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/Circle.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/Circle.java new file mode 100644 index 000000000..f40783bdd --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/Circle.java @@ -0,0 +1,80 @@ +/* + * 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.example.graph; + +import com.arangodb.serde.jackson.Id; +import com.arangodb.serde.jackson.Key; +import com.arangodb.serde.jackson.Rev; + +/** + * @author a-brandt + */ +class Circle { + + @Id + private String id; + + @Key + private String key; + + @Rev + private String revision; + + private String label; + + public Circle(String key, String label) { + this.key = key; + this.label = label; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getRevision() { + return revision; + } + + public void setRevision(String revision) { + this.revision = revision; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/CircleEdge.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/CircleEdge.java new file mode 100644 index 000000000..96afa9f00 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/CircleEdge.java @@ -0,0 +1,122 @@ +/* + * 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.example.graph; + +import com.arangodb.serde.jackson.*; + +/** + * @author a-brandt + */ +class CircleEdge { + + @Id + private String id; + + @Key + private String key; + + @Rev + private String revision; + + @From + private String from; + + @To + private String to; + + private Boolean theFalse; + private Boolean theTruth; + private String label; + + public CircleEdge(final String from, final String to, final Boolean theFalse, final Boolean theTruth, + final String label) { + this.from = from; + this.to = to; + this.theFalse = theFalse; + this.theTruth = theTruth; + this.label = label; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getRevision() { + return revision; + } + + public void setRevision(String revision) { + this.revision = revision; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public Boolean getTheFalse() { + return theFalse; + } + + public void setTheFalse(Boolean theFalse) { + this.theFalse = theFalse; + } + + public Boolean getTheTruth() { + return theTruth; + } + + public void setTheTruth(Boolean theTruth) { + this.theTruth = theTruth; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/GraphTraversalsInAQLExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/GraphTraversalsInAQLExampleTest.java new file mode 100644 index 000000000..94e48a307 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/GraphTraversalsInAQLExampleTest.java @@ -0,0 +1,113 @@ +/* + * 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.example.graph; + +import com.arangodb.ArangoCursor; +import com.arangodb.ArangoDBException; +import org.junit.jupiter.api.Test; + +import java.util.Collection; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * Graph traversals in AQL + * + * @author a-brandt + * @see Graph traversals in AQL + */ +class GraphTraversalsInAQLExampleTest extends BaseGraphTest { + + @Test + void queryAllVertices() throws ArangoDBException { + String queryString = "FOR v IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' RETURN v._key"; + ArangoCursor cursor = db.query(queryString, null, null, String.class); + Collection result = cursor.asListRemaining(); + assertThat(result).hasSize(10); + + queryString = "WITH circles FOR v IN 1..3 OUTBOUND 'circles/A' edges RETURN v._key"; + cursor = db.query(queryString, null, null, String.class); + result = cursor.asListRemaining(); + assertThat(result).hasSize(10); + } + + @Test + void queryDepthTwo() throws ArangoDBException { + String queryString = "FOR v IN 2..2 OUTBOUND 'circles/A' GRAPH 'traversalGraph' return v._key"; + ArangoCursor cursor = db.query(queryString, null, null, String.class); + Collection result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("C", "E", "H", "J"); + + queryString = "FOR v IN 2 OUTBOUND 'circles/A' GRAPH 'traversalGraph' return v._key"; + cursor = db.query(queryString, null, null, String.class); + result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("C", "E", "H", "J"); + } + + @Test + void queryWithFilter() throws ArangoDBException { + String queryString = "FOR v, e, p IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' FILTER p.vertices[1]" + + "._key != 'G' RETURN v._key"; + ArangoCursor cursor = db.query(queryString, null, null, String.class); + Collection result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("B", "C", "D", "E", "F"); + + queryString = "FOR v, e, p IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' FILTER p.edges[0].label != " + + "'right_foo' RETURN v._key"; + cursor = db.query(queryString, null, null, String.class); + result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("B", "C", "D", "E", "F"); + + queryString = "FOR v,e,p IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' FILTER p.vertices[1]._key != 'G'" + + " FILTER p.edges[1].label != 'left_blub' return v._key"; + cursor = db.query(queryString, null, null, String.class); + + result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("B", "C", "D"); + + queryString = "FOR v,e,p IN 1..3 OUTBOUND 'circles/A' GRAPH 'traversalGraph' FILTER p.vertices[1]._key != 'G'" + + " AND p.edges[1].label != 'left_blub' return v._key"; + cursor = db.query(queryString, null, null, String.class); + result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("B", "C", "D"); + } + + @Test + void queryOutboundInbound() throws ArangoDBException { + String queryString = "FOR v IN 1..3 OUTBOUND 'circles/E' GRAPH 'traversalGraph' return v._key"; + ArangoCursor cursor = db.query(queryString, null, null, String.class); + Collection result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("F"); + + queryString = "FOR v IN 1..3 INBOUND 'circles/E' GRAPH 'traversalGraph' return v._key"; + cursor = db.query(queryString, null, null, String.class); + result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("B", "A"); + + queryString = "FOR v IN 1..3 ANY 'circles/E' GRAPH 'traversalGraph' return v._key"; + cursor = db.query(queryString, null, null, String.class); + + result = cursor.asListRemaining(); + assertThat(result).containsExactlyInAnyOrder("F", "B", "C", "D", "A", "G"); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/ShortestPathInAQLExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/ShortestPathInAQLExampleTest.java new file mode 100644 index 000000000..aecf7321a --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/graph/ShortestPathInAQLExampleTest.java @@ -0,0 +1,105 @@ +/* + * 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.example.graph; + +import com.arangodb.ArangoCursor; +import com.arangodb.ArangoDBException; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Shortest Path in AQL + * + * @author a-brandt + * @see Shortest Path in AQL + */ +class ShortestPathInAQLExampleTest extends BaseGraphTest { + + @Test + void queryShortestPathFromAToD() throws ArangoDBException { + String queryString = "FOR v, e IN OUTBOUND SHORTEST_PATH 'circles/A' TO 'circles/D' GRAPH 'traversalGraph' " + + "RETURN {'vertex': v._key, 'edge': e._key}"; + ArangoCursor cursor = db.query(queryString, null, null, Pair.class); + final Collection collection = toVertexCollection(cursor); + assertThat(collection.size()).isEqualTo(4); + assertThat(collection).containsExactlyInAnyOrder("A", "B", "C", "D"); + + queryString = "WITH circles FOR v, e IN OUTBOUND SHORTEST_PATH 'circles/A' TO 'circles/D' edges RETURN " + + "{'vertex': v._key, 'edge': e._key}"; + db.query(queryString, null, null, Pair.class); + assertThat(collection.size()).isEqualTo(4); + assertThat(collection).containsExactlyInAnyOrder("A", "B", "C", "D"); + } + + @Test + void queryShortestPathByFilter() throws ArangoDBException { + String queryString = "FOR a IN circles FILTER a._key == 'A' FOR d IN circles FILTER d._key == 'D' FOR v, e IN" + + " OUTBOUND SHORTEST_PATH a TO d GRAPH 'traversalGraph' RETURN {'vertex':v._key, 'edge':e._key}"; + ArangoCursor cursor = db.query(queryString, null, null, Pair.class); + final Collection collection = toVertexCollection(cursor); + assertThat(collection.size()).isEqualTo(4); + assertThat(collection).containsExactlyInAnyOrder("A", "B", "C", "D"); + + queryString = "FOR a IN circles FILTER a._key == 'A' FOR d IN circles FILTER d._key == 'D' FOR v, e IN " + + "OUTBOUND SHORTEST_PATH a TO d edges RETURN {'vertex': v._key, 'edge': e._key}"; + db.query(queryString, null, null, Pair.class); + assertThat(collection.size()).isEqualTo(4); + assertThat(collection).containsExactlyInAnyOrder("A", "B", "C", "D"); + } + + private Collection toVertexCollection(final ArangoCursor cursor) { + final List result = new ArrayList<>(); + while (cursor.hasNext()) { + final Pair pair = cursor.next(); + result.add(pair.getVertex()); + } + return result; + } + + public static class Pair { + + private String vertex; + private String edge; + + String getVertex() { + return vertex; + } + + void setVertex(final String vertex) { + this.vertex = vertex; + } + + String getEdge() { + return edge; + } + + void setEdge(final String edge) { + this.edge = edge; + } + + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/ssl/SslExampleTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/ssl/SslExampleTest.java new file mode 100644 index 000000000..168e4d4da --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/example/ssl/SslExampleTest.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.example.ssl; + +import com.arangodb.ArangoDB; +import com.arangodb.ArangoDBException; +import com.arangodb.ArangoDBMultipleException; +import com.arangodb.Protocol; +import com.arangodb.entity.ArangoDBVersion; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.TrustManagerFactory; +import java.security.KeyStore; +import java.util.List; + +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 + */ +@Tag("ssl") +@EnabledIfSystemProperty(named = "SslTest", matches = "true") +class SslExampleTest { + + /*- + * a SSL trust store + * + * create the trust store for the self signed certificate: + * keytool -import -alias "my arangodb server cert" -file server.pem -keystore example.truststore + * + * Documentation: + * https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLSocketFactory.html + */ + private static final String SSL_TRUSTSTORE = "/example.truststore"; + private static final String SSL_TRUSTSTORE_PASSWORD = "12345678"; + + @ParameterizedTest + @EnumSource(Protocol.class) + void connect(Protocol protocol) throws Exception { + final ArangoDB arangoDB = new ArangoDB.Builder() + .host("localhost", 8529) + .password("test") + .useSsl(true) + .sslContext(createSslContext()) + .useProtocol(protocol) + .build(); + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version).isNotNull(); + System.out.println(version.getVersion()); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void noopHostnameVerifier(Protocol protocol) throws Exception { + final ArangoDB arangoDB = new ArangoDB.Builder() + .host("127.0.0.1", 8529) + .password("test") + .useSsl(true) + .sslContext(createSslContext()) + .verifyHost(false) + .useProtocol(protocol) + .build(); + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version).isNotNull(); + System.out.println(version.getVersion()); + } + + @ParameterizedTest + @EnumSource(Protocol.class) + void hostnameVerifierFailure(Protocol protocol) throws Exception { + assumeTrue(protocol != Protocol.VST, "VST does not support hostname verification"); + final ArangoDB arangoDB = new ArangoDB.Builder() + .host("127.0.0.1", 8529) + .password("test") + .useSsl(true) + .sslContext(createSslContext()) + .verifyHost(true) + .useProtocol(protocol) + .build(); + Throwable thrown = catchThrowable(arangoDB::getVersion); + assertThat(thrown).isInstanceOf(ArangoDBException.class); + ArangoDBException ex = (ArangoDBException) thrown; + assertThat(ex.getCause()).isInstanceOf(ArangoDBMultipleException.class); + List exceptions = ((ArangoDBMultipleException) ex.getCause()).getExceptions(); + exceptions.forEach(e -> assertThat(e).isInstanceOf(SSLHandshakeException.class)); + } + + private SSLContext createSslContext() throws Exception { + final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(this.getClass().getResourceAsStream(SSL_TRUSTSTORE), SSL_TRUSTSTORE_PASSWORD.toCharArray()); + + final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, SSL_TRUSTSTORE_PASSWORD.toCharArray()); + + final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + + final SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + return sc; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/HostHandlerTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/HostHandlerTest.java new file mode 100644 index 000000000..ceff3a79e --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/HostHandlerTest.java @@ -0,0 +1,189 @@ +/* + * 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.ArangoDBMultipleException; +import com.arangodb.internal.net.*; +import com.arangodb.internal.serde.InternalSerde; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * @author Mark Vollmary + */ +class HostHandlerTest { + + private static final ConnectionPool mockCP = new ConnectionPool() { + @Override + public Connection createConnection(HostDescription host) { + return null; + } + + @Override + public Connection connection() { + return null; + } + + @Override + public void setJwt(String jwt) { + + } + + @Override + 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(); + set.addHost(HOST_0); + return set; + } + + @Override + public void init(ArangoExecutorSync executor, InternalSerde arangoSerialization) { + + } + + }; + 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(); + set.addHost(HOST_0); + set.addHost(HOST_1); + set.addHost(HOST_2); + return set; + } + + @Override + public void init(ArangoExecutorSync executor, InternalSerde arangoSerialization) { + + } + + }; + + @Test + void fallbackHostHandlerSingleHost() { + final HostHandler handler = new FallbackHostHandler(SINGLE_HOST); + assertThat(handler.get(null, null)).isEqualTo(HOST_0); + handler.fail(new RuntimeException()); + assertThat(handler.get(null, null)).isEqualTo(HOST_0); + } + + @Test + void fallbackHostHandlerMultipleHosts() { + final HostHandler handler = new FallbackHostHandler(MULTIPLE_HOSTS); + for (int i = 0; i < 3; i++) { + assertThat(handler.get(null, null)).isEqualTo(HOST_0); + handler.fail(new RuntimeException("HOST_0 failed")); + assertThat(handler.get(null, null)).isEqualTo(HOST_1); + handler.fail(new RuntimeException("HOST_1 failed")); + assertThat(handler.get(null, null)).isEqualTo(HOST_2); + handler.fail(new RuntimeException("HOST_2 failed")); + if (i < 2) { + assertThat(handler.get(null, null)).isEqualTo(HOST_0); + } else { + try { + handler.get(null, null); + fail(); + } catch (ArangoDBException e) { + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(ArangoDBMultipleException.class); + List exceptions = ((ArangoDBMultipleException) e.getCause()).getExceptions(); + assertThat(exceptions.get(0)).isInstanceOf(RuntimeException.class); + assertThat(exceptions.get(0).getMessage()).isEqualTo("HOST_0 failed"); + assertThat(exceptions.get(1)).isInstanceOf(RuntimeException.class); + assertThat(exceptions.get(1).getMessage()).isEqualTo("HOST_1 failed"); + assertThat(exceptions.get(2)).isInstanceOf(RuntimeException.class); + assertThat(exceptions.get(2).getMessage()).isEqualTo("HOST_2 failed"); + } + } + } + } + + @Test + void randomHostHandlerSingleHost() { + final HostHandler handler = new RandomHostHandler(SINGLE_HOST, new FallbackHostHandler(SINGLE_HOST)); + assertThat(handler.get(null, null)).isEqualTo(HOST_0); + handler.fail(new RuntimeException()); + assertThat(handler.get(null, null)).isEqualTo(HOST_0); + } + + @Test + void randomHostHandlerMultipleHosts() { + final HostHandler handler = new RandomHostHandler(MULTIPLE_HOSTS, new FallbackHostHandler(MULTIPLE_HOSTS)); + + final Host pick0 = handler.get(null, null); + assertThat(pick0).isIn(HOST_0, HOST_1, HOST_2); + handler.fail(new RuntimeException()); + + final Host pick1 = handler.get(null, null); + assertThat(pick1).isIn(HOST_0, HOST_1, HOST_2); + handler.success(); + + final Host pick3 = handler.get(null, null); + assertThat(pick3) + .isIn(HOST_0, HOST_1, HOST_2) + .isEqualTo(pick1); + } + + @Test + void roundRobinHostHandlerSingleHost() { + final HostHandler handler = new RoundRobinHostHandler(SINGLE_HOST); + assertThat(handler.get(null, null)).isEqualTo(HOST_0); + handler.fail(new RuntimeException()); + assertThat(handler.get(null, null)).isEqualTo(HOST_0); + } + + @Test + void roundRobinHostHandlerMultipleHosts() { + final HostHandler handler = new RoundRobinHostHandler(MULTIPLE_HOSTS); + final Host pick0 = handler.get(null, null); + assertThat(pick0).isIn(HOST_0, HOST_1, HOST_2); + final Host pick1 = handler.get(null, null); + assertThat(pick1) + .isIn(HOST_0, HOST_1, HOST_2) + .isNotEqualTo(pick0); + final Host pick2 = handler.get(null, null); + assertThat(pick2) + .isIn(HOST_0, HOST_1, HOST_2) + .isNotIn(pick0, pick1); + final Host pick4 = handler.get(null, null); + assertThat(pick4).isEqualTo(pick0); + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/QueueTimeMetricsImplTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/QueueTimeMetricsImplTest.java new file mode 100644 index 000000000..7c4ca4c8a --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/QueueTimeMetricsImplTest.java @@ -0,0 +1,61 @@ +package com.arangodb.internal; + +import com.arangodb.model.QueueTimeSample; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Random; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.within; + + +class QueueTimeMetricsImplTest { + private final static int QSIZE = 1024; + private final Random rnd = new Random(); + private final QueueTimeMetricsImpl q = new QueueTimeMetricsImpl(QSIZE); + + @Test + void halfSizeTest() { + testQueue(QSIZE / 2); + } + + @Test + void fullSizeTest() { + testQueue(QSIZE); + } + + @Test + void emptySizeTest() { + testQueue(0); + } + + @Test + void overSizeTest() { + testQueue((int) (QSIZE * 1.2)); + testQueue((int) (QSIZE * 3000.4)); + } + + private void testQueue(int size) { + q.clear(); + for (int i = 0; i < size; i++) { + q.add(new QueueTimeSample(i, rnd.nextDouble())); + } + QueueTimeSample[] values = q.getValues(); + assertThat(values).hasSize(Math.min(size, QSIZE)); + assertThat(q.getAvg()).isEqualTo(getAvg(values), within(1.0E-12)); + assertThat(q.getAvg()).isGreaterThanOrEqualTo(0.0); + + for (int i = 0; i < values.length; i++) { + assertThat(values[i]).isNotNull(); + if (i > 0) { + assertThat(values[i].timestamp).isGreaterThan(values[i - 1].timestamp); + } + } + } + + private double getAvg(QueueTimeSample[] elements) { + return Arrays.stream(elements).mapToDouble(it -> it.value).average().orElse(0.0); + } + +} \ No newline at end of file diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/velocystream/CommunicationTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/velocystream/CommunicationTest.java new file mode 100644 index 000000000..bfdeb444a --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/internal/velocystream/CommunicationTest.java @@ -0,0 +1,122 @@ +/* + * 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.velocystream; + +import com.arangodb.ArangoDB; +import com.arangodb.ArangoDatabase; +import com.arangodb.entity.ArangoDBVersion; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import org.junit.jupiter.api.Test; + +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedQueue; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Mark Vollmary + */ +class CommunicationTest { + + private static final String FAST = "fast"; + private static final String SLOW = "slow"; + + @Test + void chunkSizeSmall() { + final ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .chunksize(20).build(); + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version).isNotNull(); + } + + @Test + void multiThread() throws Exception { + final ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + arangoDB.getUsers(); // authentication and active-failover connection redirect to master + + final Collection result = new ConcurrentLinkedQueue<>(); + final Thread fast = new Thread(() -> { + arangoDB.db().query("return sleep(0.1)", null, null, null); + result.add(FAST); + }); + final Thread slow = new Thread(() -> { + arangoDB.db().query("return sleep(0.5)", null, null, null); + result.add(SLOW); + }); + slow.start(); + fast.start(); + + slow.join(); + fast.join(); + + assertThat(result.size()).isEqualTo(2); + final Iterator iterator = result.iterator(); + assertThat(iterator.next()).isEqualTo(FAST); + assertThat(iterator.next()).isEqualTo(SLOW); + } + + @Test + void multiThreadSameDatabases() throws Exception { + final ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + arangoDB.getUsers(); // authentication and active-failover connection redirect to master + + final ArangoDatabase db = arangoDB.db(); + + final Collection result = new ConcurrentLinkedQueue<>(); + final Thread t1 = new Thread(() -> { + db.query("return sleep(0.1)", null, null, null); + result.add("1"); + }); + final Thread t2 = new Thread(() -> { + db.query("return sleep(0.1)", null, null, null); + result.add("1"); + }); + t2.start(); + t1.start(); + t2.join(); + t1.join(); + assertThat(result.size()).isEqualTo(2); + } + + @Test + void minOneConnection() { + final ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .maxConnections(0).build(); + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version).isNotNull(); + } + + @Test + void defaultMaxConnection() { + final ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .maxConnections(null).build(); + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version).isNotNull(); + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/CustomSerdeTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/CustomSerdeTest.java new file mode 100644 index 000000000..428363da6 --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/CustomSerdeTest.java @@ -0,0 +1,242 @@ +/* + * 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.internal.config.FileConfigPropertiesProvider; +import com.arangodb.internal.serde.InternalSerde; +import com.arangodb.model.DocumentCreateOptions; +import com.arangodb.serde.jackson.JacksonSerde; +import com.arangodb.serde.jackson.JacksonSerdeProvider; +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 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 CustomSerdeTest { + + 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 ArangoDB arangoDB; + private static ArangoDatabase db; + private static ArangoCollection collection; + + @BeforeAll + static void init() { + JacksonSerde serde = new JacksonSerdeProvider().of(ContentType.VPACK); + serde.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(new FileConfigPropertiesProvider()) + .useProtocol(Protocol.VST) + .serde(serde).build(); + + db = arangoDB.db(DbName.of("custom-serde-test")); + if (!db.exists()) { + db.create(); + } + + collection = db.collection(COLLECTION_NAME); + if (!collection.exists()) { + collection.create(); + } + } + + @AfterAll + static void shutdown() { + if (db.exists()) + db.drop(); + } + + @Test + void customPersonDeserializer() { + Person person = new Person(); + person.name = "Joe"; + Person result = collection.insertDocument( + person, + new DocumentCreateOptions().returnNew(true) + ).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() { + 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", + params, + Map.class + ).next(); + + 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() { + 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)", + Collections.singletonMap("docId", COLLECTION_NAME + "/" + key), + Map.class + ).next(); + + 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() { + 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) + ).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() { + 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.collection(COLLECTION_NAME).getDocument( + key, + Map.class, + null); + + 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; + } + +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/CustomTypeHintTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/CustomTypeHintTest.java new file mode 100644 index 000000000..fed63354e --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/CustomTypeHintTest.java @@ -0,0 +1,145 @@ +/* + * 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.ArangoCollection; +import com.arangodb.ArangoDB; +import com.arangodb.ArangoDatabase; +import com.arangodb.DbName; +import com.arangodb.internal.config.FileConfigPropertiesProvider; +import com.arangodb.model.DocumentCreateOptions; +import com.arangodb.serde.jackson.Key; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Michele Rastelli + */ +class CustomTypeHintTest { + + private static final String COLLECTION_NAME = "collection"; + private ArangoDatabase db; + private ArangoCollection collection; + + @BeforeEach + void init() { + ArangoDB arangoDB = new ArangoDB.Builder() + .loadProperties(new FileConfigPropertiesProvider()) + .build(); + + db = arangoDB.db(DbName.of("custom-serde-test")); + if (!db.exists()) { + db.create(); + } + + collection = db.collection(COLLECTION_NAME); + if (!collection.exists()) { + collection.create(); + } + } + + @AfterEach + void shutdown() { + if (db.exists()) + db.drop(); + } + + @Test + void insertDocument() { + Gorilla gorilla = new Gorilla(); + gorilla.setName("kingKong"); + + Zoo doc = new Zoo(); + doc.setAnimal(gorilla); + + Zoo insertedDoc = collection.insertDocument( + doc, + new DocumentCreateOptions().returnNew(true) + ).getNew(); + + assertThat((insertedDoc.getAnimal().getName())).isEqualTo("kingKong"); + + String key = insertedDoc.getKey(); + + // in the db a document like this is created: + // { + // "animal": { + // "type": "com.arangodb.serde.CustomTypeHintTest$Gorilla", + // "name": "kingKong" + // } + // } + + final Zoo readDoc = collection.getDocument( + key, + Zoo.class, + null); + + assertThat((readDoc.getAnimal().getName())).isEqualTo("kingKong"); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "type") + public interface Animal { + String getName(); + } + + public static class Gorilla implements Animal { + private String name; + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class Zoo { + + @Key + private String key; + + private Animal animal; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Animal getAnimal() { + return animal; + } + + public void setAnimal(Animal animal) { + this.animal = animal; + } + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/SerdeTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/SerdeTest.java index 8b80e637f..36daf7450 100644 --- a/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/SerdeTest.java +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/serde/SerdeTest.java @@ -1,82 +1,71 @@ package com.arangodb.serde; -import com.arangodb.ArangoDB; -import com.arangodb.BaseTest; +import com.arangodb.ContentType; +import com.arangodb.internal.serde.InternalSerde; +import com.arangodb.internal.serde.SerdeUtils; import com.arangodb.shaded.fasterxml.jackson.databind.JsonNode; import com.arangodb.shaded.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.arangodb.shaded.fasterxml.jackson.databind.node.ObjectNode; +import com.arangodb.util.RawBytes; import com.arangodb.util.RawJson; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.EnumSource; -import java.util.Collections; -import java.util.Map; +import java.sql.Date; import static org.assertj.core.api.Assertions.assertThat; -class SerdeTest extends BaseTest { - @ParameterizedTest - @MethodSource("adbByContentType") - void shadedJsonNode(ArangoDB adb) { - // uses the internal serde - JsonNode doc = JsonNodeFactory.instance - .objectNode() - .put("foo", "bar"); - JsonNode res = adb.db().query("return @d", Collections.singletonMap("d", doc), JsonNode.class).next(); - assertThat(res.size()).isEqualTo(1); - assertThat(res.get("foo").asText()).isEqualTo("bar"); - JsonNode value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), JsonNode.class).next(); - assertThat(value.textValue()).isEqualTo("bar"); - } +class SerdeTest { @ParameterizedTest - @MethodSource("adbByContentType") - void jsonNode(ArangoDB adb) { - // uses the user serde - com.fasterxml.jackson.databind.JsonNode doc = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance - .objectNode() - .put("foo", "bar"); - com.fasterxml.jackson.databind.JsonNode res = adb.db().query("return @d", Collections.singletonMap("d", doc), - com.fasterxml.jackson.databind.JsonNode.class).next(); - assertThat(res.size()).isEqualTo(1); - assertThat(res.get("foo").asText()).isEqualTo("bar"); - com.fasterxml.jackson.databind.JsonNode value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), - com.fasterxml.jackson.databind.JsonNode.class).next(); - assertThat(value.textValue()).isEqualTo("bar"); + @EnumSource(ContentType.class) + void rawJsonSerde(ContentType type) { + InternalSerde s = InternalSerde.of(type, null); + ObjectNode node = JsonNodeFactory.instance.objectNode().put("foo", "bar"); + RawJson raw = RawJson.of(SerdeUtils.INSTANCE.writeJson(node)); + byte[] serialized = s.serialize(raw); + RawJson deserialized = s.deserialize(serialized, RawJson.class); + assertThat(deserialized).isEqualTo(raw); } @ParameterizedTest - @MethodSource("adbByContentType") - void map(ArangoDB adb) { - Map doc = Collections.singletonMap("foo", "bar"); - Map res = adb.db().query("return @d", Collections.singletonMap("d", doc), Map.class).next(); - assertThat(res).hasSize(1); - assertThat(res.get("foo")).isEqualTo("bar"); - String value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), String.class).next(); - assertThat(value).isEqualTo("bar"); + @EnumSource(ContentType.class) + void rawBytesSerde(ContentType type) { + InternalSerde s = InternalSerde.of(type, null); + ObjectNode node = JsonNodeFactory.instance.objectNode().put("foo", "bar"); + RawBytes raw = RawBytes.of(s.serialize(node)); + byte[] serialized = s.serialize(raw); + RawBytes deserialized = s.deserialize(serialized, RawBytes.class); + assertThat(deserialized).isEqualTo(raw); } @ParameterizedTest - @MethodSource("adbByContentType") - void rawJson(ArangoDB adb) { - RawJson doc = RawJson.of(""" - {"foo":"bar"}"""); - RawJson res = adb.db().query("return @d", Collections.singletonMap("d", doc), RawJson.class).next(); - assertThat(res.getValue()).isEqualTo(doc.getValue()); - RawJson value = adb.db().query("return @d.foo", Collections.singletonMap("d", doc), RawJson.class).next(); - assertThat(value.getValue()).isEqualTo("\"bar\""); + @EnumSource(ContentType.class) + void utilDateSerde(ContentType type) { + InternalSerde s = InternalSerde.of(type, null); + long ts = 1000000000000L; + java.util.Date date = new java.util.Date(ts); + byte[] ser = s.serialize(date); + JsonNode node = s.parse(ser); + assertThat(node.isLong()).isTrue(); + assertThat(node.longValue()).isEqualTo(ts); + java.util.Date deser = s.deserialize(ser, java.util.Date.class); + assertThat(deser).isEqualTo(date); } @ParameterizedTest - @MethodSource("adbByContentType") - void person(ArangoDB adb) { - Person doc = new Person("key", "Jim", 22); - Person res = adb.db().query("return @d", Collections.singletonMap("d", doc), Person.class).next(); - assertThat(res).isEqualTo(doc); - String key = adb.db().query("return @d._key", Collections.singletonMap("d", doc), String.class).next(); - assertThat(key).isEqualTo("key"); - String name = adb.db().query("return @d.firstName", Collections.singletonMap("d", doc), String.class).next(); - assertThat(name).isEqualTo("Jim"); + @EnumSource(ContentType.class) + void sqlDateSerde(ContentType type) { + InternalSerde s = InternalSerde.of(type, null); + long ts = 1000000000000L; + Date date = new Date(ts); + byte[] ser = s.serialize(date); + JsonNode node = s.parse(ser); + assertThat(node.isLong()).isTrue(); + assertThat(node.longValue()).isEqualTo(ts); + Date deser = s.deserialize(ser, Date.class); + assertThat(deser).isEqualTo(date); } } diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/MapBuilder.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/MapBuilder.java new file mode 100644 index 000000000..61d05865e --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/MapBuilder.java @@ -0,0 +1,46 @@ +/* + * 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.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Mark Vollmary + */ +public class MapBuilder { + + private final Map map; + + public MapBuilder() { + super(); + map = new LinkedHashMap<>(); + } + + public MapBuilder put(final String key, final Object value) { + map.put(key, value); + return this; + } + + public Map get() { + return map; + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/MapBuilderTest.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/MapBuilderTest.java new file mode 100644 index 000000000..0496a685a --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/MapBuilderTest.java @@ -0,0 +1,43 @@ +/* + * 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.util; + + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + + +/** + * @author Mark Vollmary + */ +class MapBuilderTest { + + @Test + void build() { + final Map map = new MapBuilder().put("foo", "bar").get(); + assertThat(map).hasSize(1); + assertThat(map.get("foo")).isNotNull(); + assertThat(map).containsEntry("foo", "bar"); + } +} diff --git a/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/TestUtils.java b/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/TestUtils.java new file mode 100644 index 000000000..a8faab73a --- /dev/null +++ b/shaded-integration-tests/src/test/jackson/java/com/arangodb/util/TestUtils.java @@ -0,0 +1,112 @@ +/* + * 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.util; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.UUID; + +/** + * @author Michele Rastelli + */ +public final class TestUtils { + + private static final String[] allChars = TestUtils.generateAllInputChars(); + private static final Random r = new Random(); + + private TestUtils() { + } + + /** + * Parses {@param version} and checks whether it is greater or equal to + * <{@param otherMajor}, {@param otherMinor}, {@param otherPatch}> + * comparing the corresponding version components in lexicographical order. + */ + public static boolean isAtLeastVersion(final String version, final int otherMajor, final int otherMinor, + final int otherPatch) { + return compareVersion(version, otherMajor, otherMinor, otherPatch) >= 0; + } + + /** + * Parses {@param version} and checks whether it is less than + * <{@param otherMajor}, {@param otherMinor}, {@param otherPatch}> + * comparing the corresponding version components in lexicographical order. + */ + public static boolean isLessThanVersion(final String version, final int otherMajor, final int otherMinor, + final int otherPatch) { + return compareVersion(version, otherMajor, otherMinor, otherPatch) < 0; + } + + private static int compareVersion(final String version, final int otherMajor, final int otherMinor, + final int otherPatch) { + String[] parts = version.split("-")[0].split("\\."); + + int major = Integer.parseInt(parts[0]); + int minor = Integer.parseInt(parts[1]); + int patch = Integer.parseInt(parts[2]); + + int majorComparison = Integer.compare(major, otherMajor); + if (majorComparison != 0) { + return majorComparison; + } + + int minorComparison = Integer.compare(minor, otherMinor); + if (minorComparison != 0) { + return minorComparison; + } + + return Integer.compare(patch, otherPatch); + } + + private static String[] generateAllInputChars() { + List list = new ArrayList<>(); + for (int codePoint = 0; codePoint < Character.MAX_CODE_POINT + 1; codePoint++) { + String s = new String(Character.toChars(codePoint)); + if (codePoint == 47 || // '/' + codePoint == 58 || // ':' + Character.isISOControl(codePoint) || + Character.isLowSurrogate(s.charAt(0)) || + (Character.isHighSurrogate(s.charAt(0)) && s.length() == 1)) { + continue; + } + list.add(s); + } + return list.toArray(new String[0]); + } + + public static String generateRandomDbName(int length, boolean extendedNames) { + if (extendedNames) { + int max = allChars.length; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + String allChar = allChars[r.nextInt(max)]; + sb.append(allChar); + } + return UnicodeUtils.normalize(sb.toString()); + } else { + return UUID.randomUUID().toString(); + } + } + +} diff --git a/shaded-integration-tests/src/test/resources/arangodb-bad.properties b/shaded-integration-tests/src/test/resources/arangodb-bad.properties new file mode 100644 index 000000000..2b2743531 --- /dev/null +++ b/shaded-integration-tests/src/test/resources/arangodb-bad.properties @@ -0,0 +1 @@ +arangodb.hosts=127.0.0.1:8529,127.0.0.1:fail \ No newline at end of file diff --git a/shaded-integration-tests/src/test/resources/arangodb.properties b/shaded-integration-tests/src/test/resources/arangodb.properties new file mode 100644 index 000000000..c47741102 --- /dev/null +++ b/shaded-integration-tests/src/test/resources/arangodb.properties @@ -0,0 +1,5 @@ +arangodb.hosts=172.28.0.1:8529 +arangodb.acquireHostList=true +arangodb.password=test +arangodb.timeout=30000 +arangodb.metrics.responseQueueTimeSamples=20 diff --git a/shaded-integration-tests/src/test/resources/logback-test.xml b/shaded-integration-tests/src/test/resources/logback-test.xml new file mode 100644 index 000000000..b1d65b196 --- /dev/null +++ b/shaded-integration-tests/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + From 0cdb02474c1740eb9ef8db8a2713f2b27f6ea91d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 7 Dec 2022 21:13:45 +0100 Subject: [PATCH 15/20] archunit tests for shaded pkg --- .../src/test/jsonb/java/arch/RelocationsTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java b/shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java index ce2d0bb09..db2516a94 100644 --- a/shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java +++ b/shaded-integration-tests/src/test/jsonb/java/arch/RelocationsTest.java @@ -43,4 +43,12 @@ public class RelocationsTest { .should().dependOnClassesThat() .resideInAPackage("com.arangodb.velocypack.."); + @ArchTest + // jsonb-serde is accessed via SPI + public static final ArchRule noExplicitDependencyOnJsonbSerde = noClasses().that() + .resideInAPackage("com.arangodb..").and() + .resideOutsideOfPackage("com.arangodb.serde.jsonb..") + .should().dependOnClassesThat() + .resideInAPackage("com.arangodb.serde.jsonb.."); + } From ac614611d9e39da6b97210ac81eba642ae0c3fb3 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 8 Dec 2022 13:40:21 +0100 Subject: [PATCH 16/20] rm deactivateDefaultTyping from jackson-serde --- .../com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java index 5c6247c2a..f439d7c3e 100644 --- a/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java +++ b/jackson-serde/src/main/java/com/arangodb/serde/jackson/internal/JacksonSerdeImpl.java @@ -19,7 +19,6 @@ public final class JacksonSerdeImpl implements JacksonSerde { public JacksonSerdeImpl(final ObjectMapper mapper) { this.mapper = mapper; try { - mapper.deactivateDefaultTyping(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } catch (Exception e) { // to be safe in case the provided Jackson version does not support the methods above From 299b3c9073d5f9c92b7de2539f1d4e81b3696f89 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 8 Dec 2022 14:59:38 +0100 Subject: [PATCH 17/20] deploy config upd --- driver/pom.xml | 21 +-------------------- jackson-serde/pom.xml | 6 ++++++ jsonb-serde/pom.xml | 7 +++++++ pom.xml | 21 +++++++++++++++++++++ shaded/pom.xml | 5 +++++ 5 files changed, 40 insertions(+), 20 deletions(-) diff --git a/driver/pom.xml b/driver/pom.xml index 39391cccb..11c48edb8 100644 --- a/driver/pom.xml +++ b/driver/pom.xml @@ -17,6 +17,7 @@ arangodb-1 https://sonarcloud.io + false false @@ -128,26 +129,6 @@ - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - true - - ossrh - https://oss.sonatype.org/ - 84aff6e87e214c - false - - - - org.apache.maven.plugins - maven-deploy-plugin - 3.0.0 - - 10 - - org.jacoco jacoco-maven-plugin diff --git a/jackson-serde/pom.xml b/jackson-serde/pom.xml index 2681352c8..7ce85c92e 100644 --- a/jackson-serde/pom.xml +++ b/jackson-serde/pom.xml @@ -9,7 +9,13 @@ 7.0.0-SNAPSHOT + jackson-serde jackson-serde + Jackson Serde module for ArangoDB Java Driver + + + false + diff --git a/jsonb-serde/pom.xml b/jsonb-serde/pom.xml index 6be681942..3776e7e3a 100644 --- a/jsonb-serde/pom.xml +++ b/jsonb-serde/pom.xml @@ -9,7 +9,14 @@ 7.0.0-SNAPSHOT + jsonb-serde jsonb-serde + JsonB Serde module for ArangoDB Java Driver + + + + true + diff --git a/pom.xml b/pom.xml index ad9dd2f4f..98ee1dd3a 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ 1.8 1.8 UTF-8 + true 2.14.1 @@ -281,6 +282,26 @@ + + org.apache.maven.plugins + maven-deploy-plugin + 3.0.0 + + 10 + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + true + + ossrh + https://oss.sonatype.org/ + 84aff6e87e214c + false + + diff --git a/shaded/pom.xml b/shaded/pom.xml index 6ed69a89c..132d82030 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -3,6 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + com.arangodb arangodb-java-driver-parent @@ -13,6 +14,10 @@ arangodb-java-driver-shaded ArangoDB Java Driver Shaded + + false + + com.arangodb From fb181e076ba6a3a4339314714d72823a822eb6d7 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 8 Dec 2022 15:16:03 +0100 Subject: [PATCH 18/20] deploy config upd --- pom.xml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 98ee1dd3a..008081829 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,6 @@ shaded jackson-serde serde-api - jsonb-serde pom @@ -322,4 +321,18 @@ scm:git:git://github.com/arangodb/arangodb-java-driver.git + + + no-deploy + + + !deploy + + + + jsonb-serde + + + + From c9b9ce38ce2c6b12d5853610f7ed0e68b9eb7367 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 8 Dec 2022 15:52:10 +0100 Subject: [PATCH 19/20] fixed shaded serde-api --- shaded/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shaded/pom.xml b/shaded/pom.xml index 132d82030..1c0334a67 100644 --- a/shaded/pom.xml +++ b/shaded/pom.xml @@ -22,6 +22,12 @@ com.arangodb arangodb-java-driver + + + com.arangodb + serde-api + + com.arangodb From c45ee6799e6af39c289d2ef04842d0d1924ba617 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 8 Dec 2022 21:07:48 +0100 Subject: [PATCH 20/20] shaded-integration-tests CI --- .github/workflows/maven.yml | 38 ++++++++++++++++++++++++++++++++ shaded-integration-tests/pom.xml | 4 ++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 4ff1b9444..7d9b0eb40 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -263,3 +263,41 @@ jobs: working-directory: driver run: mvn -B --no-transfer-progress -Dgpg.skip=true verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=arangodb_arangodb-java-driver + shaded-integration-tests: + timeout-minutes: 20 + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + docker-img: + - docker.io/arangodb/arangodb:3.10.1 + topology: + - single + - cluster + java-version: + - 17 + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK + uses: actions/setup-java@v2 + with: + java-version: ${{matrix.java-version}} + distribution: 'adopt' + cache: maven + - name: Start Database + run: ./docker/start_db.sh + env: + STARTER_MODE: ${{matrix.topology}} + DOCKER_IMAGE: ${{matrix.docker-img}} + - name: Info + run: mvn -version + - name: Install + run: mvn --no-transfer-progress install -DskipTests=true -Dgpg.skip=true -Dmaven.javadoc.skip=true + - name: Test jackson-serde + working-directory: shaded-integration-tests + run: mvn --no-transfer-progress -P jackson-serde test + - name: Test jsonb-serde + working-directory: shaded-integration-tests + run: mvn --no-transfer-progress -P jsonb-serde test diff --git a/shaded-integration-tests/pom.xml b/shaded-integration-tests/pom.xml index 563f643ee..b2f8c0331 100644 --- a/shaded-integration-tests/pom.xml +++ b/shaded-integration-tests/pom.xml @@ -9,8 +9,8 @@ 7.0.0-SNAPSHOT - 19 - 19 + 17 + 17 UTF-8 2.14.1