From df39f5e2fbaa251e4604a34deda9046e0d9fd42f Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Tue, 26 Dec 2023 20:19:02 -0300 Subject: [PATCH 01/70] build: add jnosql document api support Signed-off-by: Maximillian Arruda --- jnosql-dynamodb/pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/jnosql-dynamodb/pom.xml b/jnosql-dynamodb/pom.xml index 7f174f93a..5e48caac2 100644 --- a/jnosql-dynamodb/pom.xml +++ b/jnosql-dynamodb/pom.xml @@ -30,6 +30,14 @@ org.eclipse.jnosql.mapping jnosql-mapping-key-value + + org.eclipse.jnosql.communication + jnosql-communication-document + + + org.eclipse.jnosql.mapping + jnosql-mapping-document + ${project.groupId} jnosql-database-commons From 1ab5c8e51fd46336aac85100fa206fecd3b0401a Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Tue, 26 Dec 2023 20:30:11 -0300 Subject: [PATCH 02/70] feat: add document configuration for dynamodb Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentConfiguration.java | 28 +++++++++++++++++++ ...mmunication.document.DocumentConfiguration | 1 + 2 files changed, 29 insertions(+) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java create mode 100644 jnosql-dynamodb/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.document.DocumentConfiguration diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java new file mode 100644 index 000000000..c18ee1e67 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.document.DocumentConfiguration; +import org.eclipse.jnosql.communication.document.DocumentManagerFactory; + +public class DynamoDBDocumentConfiguration extends DynamoDBConfiguration + implements DocumentConfiguration { + @Override + public DocumentManagerFactory apply(Settings settings) { + return null; + } +} diff --git a/jnosql-dynamodb/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.document.DocumentConfiguration b/jnosql-dynamodb/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.document.DocumentConfiguration new file mode 100644 index 000000000..3d6d49086 --- /dev/null +++ b/jnosql-dynamodb/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.document.DocumentConfiguration @@ -0,0 +1 @@ +org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentConfiguration \ No newline at end of file From cd7aea61e6a2767acb59ec1d793b5e37ed0fd95c Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Tue, 26 Dec 2023 20:30:22 -0300 Subject: [PATCH 03/70] test: add test for document configuration for dynamodb Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentConfigurationTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java new file mode 100644 index 000000000..a713e83f8 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ +package org.eclipse.jnosql.databases.dynamodb.communication; + + +import org.eclipse.jnosql.communication.document.DocumentConfiguration; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; + +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) +class DynamoDBDocumentConfigurationTest { + + @Test + void shouldReturnFromConfiguration() { + var configuration = DocumentConfiguration.getConfiguration(); + Assertions.assertNotNull(configuration); + Assertions.assertTrue(configuration instanceof DynamoDBDocumentConfiguration); + } + + @Test + void shouldReturnFromConfigurationQuery() { + var configuration = DocumentConfiguration + .getConfiguration(DynamoDBDocumentConfiguration.class); + Assertions.assertNotNull(configuration); + Assertions.assertTrue(configuration instanceof DynamoDBDocumentConfiguration); + } +} From 8a7bc570570d745a323ff9edd42c0ca15a2b811f Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Wed, 27 Dec 2023 03:08:38 -0300 Subject: [PATCH 04/70] test: fix test execution with amazon/dynamodb-local:latest Signed-off-by: Maximillian Arruda --- .../DynamoDBKeyValueEntityManagerTest.java | 20 ++++++++-------- .../communication/DynamoDBTestUtils.java | 23 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBKeyValueEntityManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBKeyValueEntityManagerTest.java index 0ff1beece..94fc7a572 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBKeyValueEntityManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBKeyValueEntityManagerTest.java @@ -38,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) -public class DynamoDBKeyValueEntityManagerTest { +class DynamoDBKeyValueEntityManagerTest { private BucketManager keyValueEntityManager; @@ -53,14 +53,14 @@ public class DynamoDBKeyValueEntityManagerTest { private KeyValueEntity keyValueSoro = KeyValueEntity.of("soro", Value.of(userSoro)); @BeforeEach - public void init() { - keyValueEntityManagerFactory = DynamoDBTestUtils.INSTANCE.get(); + void init() { + keyValueEntityManagerFactory = DynamoDBTestUtils.INSTANCE.getBucketManagerFactory(); keyValueEntityManager = keyValueEntityManagerFactory.apply("users-entity"); } @Test - public void shouldPutValue() { + void shouldPutValue() { keyValueEntityManager.put("otavio", userOtavio); Optional otavio = keyValueEntityManager.get("otavio"); assertTrue(otavio.isPresent()); @@ -68,7 +68,7 @@ public void shouldPutValue() { } @Test - public void shouldPutKeyValue() { + void shouldPutKeyValue() { keyValueEntityManager.put(keyValueOtavio); Optional otavio = keyValueEntityManager.get("otavio"); assertTrue(otavio.isPresent()); @@ -76,7 +76,7 @@ public void shouldPutKeyValue() { } @Test - public void shouldPutIterableKeyValue() { + void shouldPutIterableKeyValue() { keyValueEntityManager.put(asList(keyValueSoro, keyValueOtavio)); Optional otavio = keyValueEntityManager.get("otavio"); @@ -89,7 +89,7 @@ public void shouldPutIterableKeyValue() { } @Test - public void shouldMultiGet() { + void shouldMultiGet() { User user = new User("otavio"); KeyValueEntity keyValue = KeyValueEntity.of("otavio", Value.of(user)); keyValueEntityManager.put(keyValue); @@ -97,7 +97,7 @@ public void shouldMultiGet() { } @Test - public void shouldRemoveKey() { + void shouldRemoveKey() { keyValueEntityManager.put(keyValueOtavio); assertTrue(keyValueEntityManager.get("otavio").isPresent()); keyValueEntityManager.delete("otavio"); @@ -105,7 +105,7 @@ public void shouldRemoveKey() { } @Test - public void shouldRemoveMultiKey() { + void shouldRemoveMultiKey() { keyValueEntityManager.put(asList(keyValueSoro, keyValueOtavio)); List keys = asList("otavio", "soro"); Iterable values = keyValueEntityManager.get(keys); @@ -117,7 +117,7 @@ public void shouldRemoveMultiKey() { } @AfterAll - public static void shutDown() { + static void shutDown() { DynamoDBTestUtils.INSTANCE.shutDown(); } diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index 6ad74b0da..1d0033746 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -19,9 +19,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; -import java.util.function.Supplier; - -public enum DynamoDBTestUtils implements Supplier { +enum DynamoDBTestUtils { INSTANCE; @@ -30,15 +28,22 @@ public enum DynamoDBTestUtils implements Supplier { .withExposedPorts(8000) .waitingFor(Wait.defaultWaitStrategy()); - public BucketManagerFactory get() { - dynamodb.start(); + BucketManagerFactory getBucketManagerFactory() { + Settings settings = getSettings(); DynamoDBKeyValueConfiguration configuration = new DynamoDBKeyValueConfiguration(); - String endpoint = "http://" + dynamodb.getHost() + ":" + dynamodb.getFirstMappedPort(); - return configuration.apply(Settings.builder() - .put(DynamoDBConfigurations.ENDPOINT, endpoint).build()); + return configuration.apply(settings); + } + + Settings getSettings() { + dynamodb.start(); + return Settings.builder() + .put(DynamoDBConfigurations.ENDPOINT, "http://" + dynamodb.getHost() + ":" + dynamodb.getFirstMappedPort()) + .put(DynamoDBConfigurations.AWS_ACCESSKEY, System.getProperty("AWS_ACCESS_KEY_ID","DUMMYIDEXAMPLE")) + .put(DynamoDBConfigurations.AWS_SECRET_ACCESS, System.getProperty("AWS_SECRET_ACCESS_KEY","DUMMYEXAMPLEKEY")) + .put(DynamoDBConfigurations.REGION, "us-west-2").build(); } - public void shutDown() { + void shutDown() { dynamodb.close(); } } From 19fe36503ad8a040ef4908463ad8e005293bbace Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Wed, 27 Dec 2023 03:09:24 -0300 Subject: [PATCH 05/70] test: add more tests to DynamoDBDocumentConfiguration Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentConfigurationTest.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java index a713e83f8..fe87bd8fa 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java @@ -15,11 +15,14 @@ package org.eclipse.jnosql.databases.dynamodb.communication; +import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.communication.document.DocumentConfiguration; +import org.eclipse.jnosql.communication.document.DocumentManagerFactory; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; @@ -27,17 +30,38 @@ class DynamoDBDocumentConfigurationTest { @Test - void shouldReturnFromConfiguration() { + void shouldReturnFromServiceLoaderConfiguration() { var configuration = DocumentConfiguration.getConfiguration(); Assertions.assertNotNull(configuration); - Assertions.assertTrue(configuration instanceof DynamoDBDocumentConfiguration); + Assertions.assertInstanceOf(DynamoDBDocumentConfiguration.class, configuration); } @Test - void shouldReturnFromConfigurationQuery() { + void shouldReturnFromServiceLoaderConfigurationQuery() { var configuration = DocumentConfiguration .getConfiguration(DynamoDBDocumentConfiguration.class); Assertions.assertNotNull(configuration); - Assertions.assertTrue(configuration instanceof DynamoDBDocumentConfiguration); } + + @Test + void shouldReturnDocumentManagerFactory() { + var configuration = DocumentConfiguration + .getConfiguration(DynamoDBDocumentConfiguration.class); + + var settings = DynamoDBTestUtils.INSTANCE.getSettings(); + + assertSoftly(softly -> { + softly.assertThat(configuration) + .describedAs("DocumentConfiguration.getConfiguration(DynamoDBDocumentConfiguration.class) must return a non-null instance") + .isNotNull(); + + DocumentManagerFactory documentManagerFactory = configuration.apply(settings); + + softly.assertThat(documentManagerFactory) + .describedAs("DynamoDBDocumentConfiguration.apply(Settings.class) should returns a non-null DocumentManagerFactory instance") + .isNotNull(); + + }); + } + } From cb47741aba18c6bbef50b7051bb66175de6246a1 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Wed, 27 Dec 2023 03:10:10 -0300 Subject: [PATCH 06/70] chore: create DocumentManagerFactory Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentConfiguration.java | 3 +- .../DynamoDBDocumentManagerFactory.java | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java index c18ee1e67..1c3fe02bf 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java @@ -23,6 +23,7 @@ public class DynamoDBDocumentConfiguration extends DynamoDBConfiguration implements DocumentConfiguration { @Override public DocumentManagerFactory apply(Settings settings) { - return null; + var dynamoDB = getDynamoDB(settings); + return new DynamoDBDocumentManagerFactory(dynamoDB); } } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java new file mode 100644 index 000000000..dd80a84af --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.document.DocumentManager; +import org.eclipse.jnosql.communication.document.DocumentManagerFactory; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + +import java.util.Optional; + +public class DynamoDBDocumentManagerFactory implements DocumentManagerFactory { + + private final DynamoDbClient dynamoDB; + + public DynamoDBDocumentManagerFactory(DynamoDbClient dynamoDB) { + this.dynamoDB = dynamoDB; + } + + @Override + public DocumentManager apply(String s) { + return null; + } + + @Override + public void close() { + Optional.ofNullable(this.dynamoDB).ifPresent(DynamoDbClient::close); + } +} From f27fa801d7c88c9ae08527308ffad5dc61c1216e Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Wed, 27 Dec 2023 21:42:02 -0300 Subject: [PATCH 07/70] test: add DocumentManagerFactory factory for tests Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DynamoDBTestUtils.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index 1d0033746..97220dc23 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -15,6 +15,7 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.document.DocumentManagerFactory; import org.eclipse.jnosql.communication.keyvalue.BucketManagerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -34,6 +35,12 @@ BucketManagerFactory getBucketManagerFactory() { return configuration.apply(settings); } + DocumentManagerFactory getDocumentManagerFactory() { + Settings settings = getSettings(); + var configuration = new DynamoDBDocumentConfiguration(); + return configuration.apply(settings); + } + Settings getSettings() { dynamodb.start(); return Settings.builder() From 502ae0f8f6f0774a0205e57edae8978d941141c4 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Wed, 27 Dec 2023 21:42:34 -0300 Subject: [PATCH 08/70] test: add tests for DynamoDBDocumentManagerFactory Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManagerFactoryTest.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java new file mode 100644 index 000000000..7dafdf666 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.document.DocumentManagerFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; + +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) +class DynamoDBDocumentManagerFactoryTest { + + private DocumentManagerFactory documentManagerFactory; + + @BeforeEach + void setup() { + this.documentManagerFactory = DynamoDBTestUtils.INSTANCE.getDocumentManagerFactory(); + assertSoftly(softly -> { + softly.assertThat(documentManagerFactory).isNotNull(); + softly.assertThat(documentManagerFactory).isInstanceOf(DynamoDBDocumentManagerFactory.class); + }); + } + + @Test + void shouldCreateDocumentManager() { + var documentManager = documentManagerFactory.apply("anydatabase"); + assertSoftly(softly -> { + softly.assertThat(documentManager).isNotNull(); + }); + } + +} From 5b505e951f1c1463db4048f5420033273a72cf18 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 02:49:09 -0300 Subject: [PATCH 09/70] build: add dynamodb-enhanced dependency Signed-off-by: Maximillian Arruda --- jnosql-dynamodb/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jnosql-dynamodb/pom.xml b/jnosql-dynamodb/pom.xml index 5e48caac2..1df55fea3 100644 --- a/jnosql-dynamodb/pom.xml +++ b/jnosql-dynamodb/pom.xml @@ -48,6 +48,11 @@ dynamodb ${dynamodb.version} + + software.amazon.awssdk + dynamodb-enhanced + ${dynamodb.version} + org.testcontainers testcontainers From 95dda381672045b095931b79b74603255ac5c61a Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 02:50:47 -0300 Subject: [PATCH 10/70] refactor: rename the return type Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DynamoDBDocumentConfiguration.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java index 1c3fe02bf..6612aefde 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java @@ -17,12 +17,11 @@ import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.communication.document.DocumentConfiguration; -import org.eclipse.jnosql.communication.document.DocumentManagerFactory; public class DynamoDBDocumentConfiguration extends DynamoDBConfiguration implements DocumentConfiguration { @Override - public DocumentManagerFactory apply(Settings settings) { + public DynamoDBDocumentManagerFactory apply(Settings settings) { var dynamoDB = getDynamoDB(settings); return new DynamoDBDocumentManagerFactory(dynamoDB); } From 651d13668efe0d30f6617fab3bb69ed8aa9d9504 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 02:53:14 -0300 Subject: [PATCH 11/70] chore: added DynamoDBDocumentManager class Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManager.java | 118 ++++++++++++++++++ .../DynamoDBDocumentManagerFactory.java | 4 +- 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java new file mode 100644 index 000000000..d93b2c3ef --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.document.*; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + +import java.time.Duration; +import java.util.Optional; +import java.util.stream.Stream; + +public class DynamoDBDocumentManager implements DocumentManager { + + private final String database; + private final DynamoDbClient dynamoDbClient; + + public DynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient) { + this.database = database; + this.dynamoDbClient = dynamoDbClient; + } + + public DynamoDbClient getDynamoDbClient() { + return dynamoDbClient; + } + + @Override + public String name() { + return database; + } + + @Override + public DocumentEntity insert(DocumentEntity documentEntity) { + return null; + } + + @Override + public DocumentEntity insert(DocumentEntity documentEntity, Duration duration) { + return null; + } + + @Override + public Iterable insert(Iterable iterable) { + return null; + } + + @Override + public Iterable insert(Iterable iterable, Duration duration) { + return null; + } + + @Override + public DocumentEntity update(DocumentEntity documentEntity) { + return null; + } + + @Override + public Iterable update(Iterable iterable) { + return null; + } + + @Override + public void delete(DocumentDeleteQuery documentDeleteQuery) { + + } + + @Override + public Stream select(DocumentQuery documentQuery) { + return null; + } + + @Override + public long count(DocumentQuery query) { + return DocumentManager.super.count(query); + } + + @Override + public boolean exists(DocumentQuery query) { + return DocumentManager.super.exists(query); + } + + @Override + public Stream query(String query) { + return DocumentManager.super.query(query); + } + + @Override + public DocumentPreparedStatement prepare(String query) { + return DocumentManager.super.prepare(query); + } + + @Override + public Optional singleResult(DocumentQuery query) { + return DocumentManager.super.singleResult(query); + } + + @Override + public long count(String s) { + return 0; + } + + @Override + public void close() { + this.dynamoDbClient.close(); + } +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java index dd80a84af..e132c3d28 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java @@ -30,8 +30,8 @@ public DynamoDBDocumentManagerFactory(DynamoDbClient dynamoDB) { } @Override - public DocumentManager apply(String s) { - return null; + public DocumentManager apply(String database) { + return new DynamoDBDocumentManager(database, dynamoDB); } @Override From 89ea227897310998e511738d8aa75d50203786b1 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 02:54:02 -0300 Subject: [PATCH 12/70] refactor: rename test class Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManagerTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java new file mode 100644 index 000000000..e51ecda92 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + + +import org.assertj.core.api.Assertions; +import org.eclipse.jnosql.communication.document.DocumentManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; + +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) +class DynamoDBDocumentManagerTest { + + private DocumentManager documentManager; + + @BeforeEach + void setUp() { + documentManager = DynamoDBTestUtils.INSTANCE.getDocumentManagerFactory().apply("default"); + Assertions.assertThat(documentManager).isNotNull(); + } + + @Test + void shouldReturnName(){ + Assertions.assertThat(documentManager.name()).isEqualTo("default"); + } + +} From 0d2ee13faadd5218f9e240d855312d761326fb6a Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 02:54:34 -0300 Subject: [PATCH 13/70] test: added utility methods Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBTestUtils.java | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index 97220dc23..b446b3edf 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -15,10 +15,19 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.communication.document.DocumentManagerFactory; import org.eclipse.jnosql.communication.keyvalue.BucketManagerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.enhanced.dynamodb.document.DocumentTableSchema; +import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; +import software.amazon.awssdk.enhanced.dynamodb.model.DescribeTableEnhancedResponse; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.*; + +import java.util.function.Function; enum DynamoDBTestUtils { @@ -35,7 +44,7 @@ BucketManagerFactory getBucketManagerFactory() { return configuration.apply(settings); } - DocumentManagerFactory getDocumentManagerFactory() { + DynamoDBDocumentManagerFactory getDocumentManagerFactory() { Settings settings = getSettings(); var configuration = new DynamoDBDocumentConfiguration(); return configuration.apply(settings); @@ -45,12 +54,43 @@ Settings getSettings() { dynamodb.start(); return Settings.builder() .put(DynamoDBConfigurations.ENDPOINT, "http://" + dynamodb.getHost() + ":" + dynamodb.getFirstMappedPort()) - .put(DynamoDBConfigurations.AWS_ACCESSKEY, System.getProperty("AWS_ACCESS_KEY_ID","DUMMYIDEXAMPLE")) - .put(DynamoDBConfigurations.AWS_SECRET_ACCESS, System.getProperty("AWS_SECRET_ACCESS_KEY","DUMMYEXAMPLEKEY")) + .put(DynamoDBConfigurations.AWS_ACCESSKEY, System.getProperty("AWS_ACCESS_KEY_ID", "DUMMYIDEXAMPLE")) + .put(DynamoDBConfigurations.AWS_SECRET_ACCESS, System.getProperty("AWS_SECRET_ACCESS_KEY", "DUMMYEXAMPLEKEY")) + .put(DynamoDBConfigurations.PROFILE, System.getProperty("AWS_PROFILE", "default")) .put(DynamoDBConfigurations.REGION, "us-west-2").build(); } void shutDown() { dynamodb.close(); } + + DynamoDbClient getDynamoDbClient() { + var settings = getSettings(); + DynamoDBBuilderSync builderSync = new DynamoDBBuilderSync(); + settings.get(DynamoDBConfigurations.ENDPOINT, String.class).ifPresent(builderSync::endpoint); + settings.get(DynamoDBConfigurations.AWS_ACCESSKEY, String.class).ifPresent(builderSync::awsAccessKey); + settings.get(DynamoDBConfigurations.AWS_SECRET_ACCESS, String.class).ifPresent(builderSync::awsSecretAccess); + settings.get(DynamoDBConfigurations.PROFILE, String.class).ifPresent(builderSync::profile); + settings.get(DynamoDBConfigurations.REGION, String.class).ifPresent(builderSync::region); + return builderSync.build(); + } + + DynamoDbEnhancedClient getDynamoDbEnhancedClient() { + return DynamoDbEnhancedClient.builder().dynamoDbClient(getDynamoDbClient()).build(); + } + + public static DescribeTableEnhancedResponse createTable(DynamoDbEnhancedClient enhancedClient, String tableName, Function documentTableSchema) { + DynamoDbTable table = enhancedClient.table(tableName, + documentTableSchema.apply(TableSchema.documentSchemaBuilder()) + .build() + ); + table.createTable(); + return table.describeTable(); + } + + public static void deleteTable(DynamoDbClient dynamoDbClient, Function deleteTableRequest) { + var request = deleteTableRequest.apply(DeleteTableRequest.builder()); + dynamoDbClient.deleteTable(request); + } + } From 185445cf9395c7840b21fa383b2201b2b3c32001 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 02:55:16 -0300 Subject: [PATCH 14/70] test: added utility class for testing purposes Signed-off-by: Maximillian Arruda --- .../DocumentEntityGenerator.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java new file mode 100644 index 000000000..5caaf34fb --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentEntity; +import org.eclipse.jnosql.communication.document.Documents; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +final class DocumentEntityGenerator { + + static final String COLLECTION_NAME = "music"; + + + static DocumentEntity getEntity() { + return getEntityWithSubDocuments(0); + } + + static DocumentEntity getEntityWithSubDocuments(int levels) { + Map map = new HashMap<>(); + map.put("_id", UUID.randomUUID().toString()); + map.put("name", "Poliana"); + map.put("hoje", LocalDate.now()); + map.put("agora", LocalDateTime.now()); + map.put("integer", 1); + map.put("float", 1f); + map.put("bigdecimal", BigDecimal.valueOf(10.10)); + map.put("city", "Salvador"); + map.put("texts",List.of("A","B","C")); + if (levels > 0) { + addSubDocument(m -> map.put("level" + levels, m), levels - 1); + } + DocumentEntity entity = DocumentEntity.of(COLLECTION_NAME); + List documents = Documents.of(map); + documents.forEach(entity::add); + return entity; + } + + static void addSubDocument(Consumer> owner, int level) { + Map map = new HashMap<>(); + map.put("level", level); + map.put("text", UUID.randomUUID().toString()); + map.put("hoje", LocalDate.now()); + map.put("agora", LocalDateTime.now()); + map.put("integer", 1); + map.put("float", 1f); + map.put("bigdecimal", BigDecimal.valueOf(10.10)); + if (level > 0) { + addSubDocument(m -> map.put("level" + level, m), level - 1); + } + owner.accept(map); + } +} \ No newline at end of file From 68605eab718076e0701718c3a02543c19411fbd1 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 02:56:10 -0300 Subject: [PATCH 15/70] chore: added DocumentEntity converter to EnhancedDocument Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentEntityConverter.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java new file mode 100644 index 000000000..0fd4baca8 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import jakarta.json.bind.Jsonb; +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentEntity; +import org.eclipse.jnosql.communication.driver.JsonbSupplier; +import org.eclipse.jnosql.communication.driver.ValueUtil; +import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.StreamSupport; + +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.toList; + +class DynamoDBDocumentEntityConverter { + + static final String ENTITY = "@entity"; + private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); + + static EnhancedDocument toEnhancedDocument(DocumentEntity documentEntity) { + return EnhancedDocument.builder() + .json(JSONB.toJson(getMap(documentEntity))) + .build(); + } + + static Map getMap(DocumentEntity entity) { + Map jsonObject = new HashMap<>(); + entity.documents().stream() + .forEach(feedJSON(jsonObject)); + jsonObject.put(ENTITY, entity.name()); + return jsonObject; + } + + private static Consumer feedJSON(Map jsonObject) { + return d -> { + Object value = ValueUtil.convert(d.value()); + if (value instanceof Document) { + Document subDocument = Document.class.cast(value); + jsonObject.put(d.name(), singletonMap(subDocument.name(), subDocument.get())); + } else if (isSudDocument(value)) { + Map subDocument = getMap(value); + jsonObject.put(d.name(), subDocument); + } else if (isSudDocumentList(value)) { + jsonObject.put(d.name(), StreamSupport.stream(Iterable.class.cast(value).spliterator(), false) + .map(DynamoDBDocumentEntityConverter::getMap).collect(toList())); + } else { + jsonObject.put(d.name(), value); + } + }; + } + + private static Map getMap(Object value) { + Map subDocument = new HashMap<>(); + StreamSupport.stream(Iterable.class.cast(value).spliterator(), + false).forEach(feedJSON(subDocument)); + return subDocument; + } + + private static boolean isSudDocument(Object value) { + return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). + allMatch(org.eclipse.jnosql.communication.document.Document.class::isInstance); + } + + private static boolean isSudDocumentList(Object value) { + return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). + allMatch(d -> d instanceof Iterable && isSudDocument(d)); + } +} From 89a3d5062c788318ecb39fbd74b2afe13997ce4e Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 02:57:07 -0300 Subject: [PATCH 16/70] test: added tests for DynamoDBDocumentEntityConverter Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentEntityConverterTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverterTest.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverterTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverterTest.java new file mode 100644 index 000000000..26cb27278 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverterTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import jakarta.json.Json; +import jakarta.json.bind.Jsonb; +import org.eclipse.jnosql.communication.driver.JsonbSupplier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import java.io.StringReader; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; + +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) +class DynamoDBDocumentEntityConverterTest { + + private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); + + + @Test + void shouldConvertDocumentEntityToEnhancedDocument() { + + assertSoftly(softly -> { + var entity = DocumentEntityGenerator.getEntity(); + var enhancedDocument = DynamoDBDocumentEntityConverter.toEnhancedDocument(entity); + var expected = Json.createReader(new StringReader(JSONB.toJson(DynamoDBDocumentEntityConverter.getMap(entity)))).readObject(); + var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); + softly.assertThat(actual).as("cannot convert a simple DocumentEntity") + .isEqualTo(expected); + }); + } + + @Test + void shouldConvertDocumentEntityWithSubDocumentsToEnhancedDocument() { + + assertSoftly(softly -> { + var entity = DocumentEntityGenerator.getEntityWithSubDocuments(3); + var enhancedDocument = DynamoDBDocumentEntityConverter.toEnhancedDocument(entity); + var expected = Json.createReader(new StringReader(JSONB.toJson(DynamoDBDocumentEntityConverter.getMap(entity)))).readObject(); + var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); + softly.assertThat(actual).as("cannot convert a DocumentEntity with document sublist") + .isEqualTo(expected); + }); + } + + +} From eec1d3ab85ff5a932da68d418d5cd7a387a2eed1 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 29 Dec 2023 03:13:27 -0300 Subject: [PATCH 17/70] refactor: fixed checkstyle issues Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBDocumentEntityConverter.java | 2 ++ .../dynamodb/communication/DynamoDBDocumentManager.java | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java index 0fd4baca8..5d2f3e34c 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java @@ -35,6 +35,8 @@ class DynamoDBDocumentEntityConverter { static final String ENTITY = "@entity"; private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); + private DynamoDBDocumentEntityConverter() {} + static EnhancedDocument toEnhancedDocument(DocumentEntity documentEntity) { return EnhancedDocument.builder() .json(JSONB.toJson(getMap(documentEntity))) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java index d93b2c3ef..1a789dd9a 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -15,7 +15,11 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.eclipse.jnosql.communication.document.*; +import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; +import org.eclipse.jnosql.communication.document.DocumentEntity; +import org.eclipse.jnosql.communication.document.DocumentManager; +import org.eclipse.jnosql.communication.document.DocumentPreparedStatement; +import org.eclipse.jnosql.communication.document.DocumentQuery; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import java.time.Duration; From 63dbff0ff6181cfb45b1244a1c7d7afa7dc8ef0d Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:37:55 -0300 Subject: [PATCH 18/70] refactor: rename classes Signed-off-by: Maximillian Arruda --- .../DocumentEntityConverter.java | 145 ++++++++++++++++++ .../DynamoDBDocumentEntityConverter.java | 88 ----------- .../DocumentEntityConverterTest.java | 118 ++++++++++++++ .../DynamoDBDocumentEntityConverterTest.java | 63 -------- 4 files changed, 263 insertions(+), 151 deletions(-) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java delete mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java delete mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverterTest.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java new file mode 100644 index 000000000..29339c2b5 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import jakarta.json.bind.Jsonb; +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentEntity; +import org.eclipse.jnosql.communication.driver.JsonbSupplier; +import org.eclipse.jnosql.communication.driver.ValueUtil; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; +import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; +import java.util.stream.StreamSupport; + +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.toList; + +class DocumentEntityConverter { + + static final String ENTITY = "@entity"; + static final String ID = "_id"; + private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); + + private DocumentEntityConverter() { + } + + static DocumentEntity toDocumentEntity(UnaryOperator entityNameResolver, EnhancedDocument enhancedDocument) { + if (enhancedDocument == null) { + return null; + } + if (enhancedDocument.toMap().isEmpty()) { + return null; + } + UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); + String entityAttribute = resolver.apply(ENTITY); + Map map = enhancedDocument.toMap(); + var entityName = map.containsKey(entityAttribute) ? map.get(entityAttribute).s() : entityAttribute; + List documents = map.entrySet() + .stream() + .filter(entry -> !Objects.equals(entityAttribute, entry.getKey())) + .map(entry -> Document.of(entry.getKey(), convertValue(entry.getValue()))) + .toList(); + return DocumentEntity.of(entityName, documents); + } + + private static Object convertValue(Object value) { + if (value instanceof AttributeValue attributeValue) { + switch (attributeValue.type()) { + case S: + return attributeValue.s(); + case N: + return Double.valueOf(attributeValue.n()); + case B: + return attributeValue.b().asByteArray(); + case SS: + return attributeValue.ss(); + case NS: + return attributeValue.ns().stream().map(Double::valueOf).toList(); + case BS: + return attributeValue.bs().stream().map(SdkBytes::asByteArray).toList(); + case L: + return attributeValue.l().stream().map(DocumentEntityConverter::convertValue).toList(); + case M: + return attributeValue.m().entrySet().stream().map(e -> Document.of(e.getKey(), convertValue(e.getValue()))).toList(); + case NUL: + return null; + case BOOL: + return attributeValue.bool(); + case UNKNOWN_TO_SDK_VERSION: + default: + return null; // map type + } + } + return value; + } + + static EnhancedDocument toEnhancedDocument(UnaryOperator entityNameResolver, DocumentEntity documentEntity) { + UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); + Map documentAsMap = getMap(resolver, documentEntity); + return EnhancedDocument.builder() + .json(JSONB.toJson(documentAsMap)) + .build(); + } + + static Map getMap(UnaryOperator entityNameResolver, DocumentEntity entity) { + var nameResolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); + Map jsonObject = new HashMap<>(); + entity.documents().forEach(feedJSON(jsonObject)); + jsonObject.put(Optional.ofNullable(nameResolver.apply(ENTITY)).orElse(ENTITY), entity.name()); + return jsonObject; + } + + private static Consumer feedJSON(Map jsonObject) { + return d -> { + Object value = ValueUtil.convert(d.value()); + if (value instanceof Document) { + Document subDocument = Document.class.cast(value); + jsonObject.put(d.name(), singletonMap(subDocument.name(), subDocument.get())); + } else if (isSudDocument(value)) { + Map subDocument = getMap(value); + jsonObject.put(d.name(), subDocument); + } else if (isSudDocumentList(value)) { + jsonObject.put(d.name(), StreamSupport.stream(Iterable.class.cast(value).spliterator(), false) + .map(DocumentEntityConverter::getMap).collect(toList())); + } else { + jsonObject.put(d.name(), value); + } + }; + } + + private static Map getMap(Object value) { + Map subDocument = new HashMap<>(); + StreamSupport.stream(Iterable.class.cast(value).spliterator(), + false).forEach(feedJSON(subDocument)); + return subDocument; + } + + private static boolean isSudDocument(Object value) { + return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). + allMatch(org.eclipse.jnosql.communication.document.Document.class::isInstance); + } + + private static boolean isSudDocumentList(Object value) { + return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). + allMatch(d -> d instanceof Iterable && isSudDocument(d)); + } +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java deleted file mode 100644 index 5d2f3e34c..000000000 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverter.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2023 Contributors to the Eclipse Foundation - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Apache License v2.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. - * - * You may elect to redistribute this code under either of these licenses. - * - * Contributors: - * - * Maximillian Arruda - */ - -package org.eclipse.jnosql.databases.dynamodb.communication; - -import jakarta.json.bind.Jsonb; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.communication.driver.JsonbSupplier; -import org.eclipse.jnosql.communication.driver.ValueUtil; -import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import java.util.stream.StreamSupport; - -import static java.util.Collections.singletonMap; -import static java.util.stream.Collectors.toList; - -class DynamoDBDocumentEntityConverter { - - static final String ENTITY = "@entity"; - private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); - - private DynamoDBDocumentEntityConverter() {} - - static EnhancedDocument toEnhancedDocument(DocumentEntity documentEntity) { - return EnhancedDocument.builder() - .json(JSONB.toJson(getMap(documentEntity))) - .build(); - } - - static Map getMap(DocumentEntity entity) { - Map jsonObject = new HashMap<>(); - entity.documents().stream() - .forEach(feedJSON(jsonObject)); - jsonObject.put(ENTITY, entity.name()); - return jsonObject; - } - - private static Consumer feedJSON(Map jsonObject) { - return d -> { - Object value = ValueUtil.convert(d.value()); - if (value instanceof Document) { - Document subDocument = Document.class.cast(value); - jsonObject.put(d.name(), singletonMap(subDocument.name(), subDocument.get())); - } else if (isSudDocument(value)) { - Map subDocument = getMap(value); - jsonObject.put(d.name(), subDocument); - } else if (isSudDocumentList(value)) { - jsonObject.put(d.name(), StreamSupport.stream(Iterable.class.cast(value).spliterator(), false) - .map(DynamoDBDocumentEntityConverter::getMap).collect(toList())); - } else { - jsonObject.put(d.name(), value); - } - }; - } - - private static Map getMap(Object value) { - Map subDocument = new HashMap<>(); - StreamSupport.stream(Iterable.class.cast(value).spliterator(), - false).forEach(feedJSON(subDocument)); - return subDocument; - } - - private static boolean isSudDocument(Object value) { - return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). - allMatch(org.eclipse.jnosql.communication.document.Document.class::isInstance); - } - - private static boolean isSudDocumentList(Object value) { - return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). - allMatch(d -> d instanceof Iterable && isSudDocument(d)); - } -} diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java new file mode 100644 index 000000000..725b083fe --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import jakarta.json.Json; +import jakarta.json.bind.Jsonb; +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.driver.JsonbSupplier; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; + +import java.io.StringReader; +import java.util.List; +import java.util.function.UnaryOperator; + +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; + +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) +class DocumentEntityConverterTest { + + private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); + private static final UnaryOperator entityNameResolver = UnaryOperator.identity(); + + + @Test + void shouldConvertDocumentEntityToEnhancedDocument() { + + assertSoftly(softly -> { + var entity = DocumentEntityGenerator.getEntity(); + var enhancedDocument = DocumentEntityConverter.toEnhancedDocument(entityNameResolver, entity); + var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); + var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); + softly.assertThat(actual).as("cannot convert a simple DocumentEntity") + .isEqualTo(expected); + }); + } + + @Test + void shouldConvertDocumentEntityWithSubDocumentsToEnhancedDocument() { + + assertSoftly(softly -> { + var entity = DocumentEntityGenerator.getEntityWithSubDocuments(3); + var enhancedDocument = DocumentEntityConverter.toEnhancedDocument(entityNameResolver, entity); + var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); + var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); + softly.assertThat(actual).as("cannot convert a DocumentEntity with document sublist") + .isEqualTo(expected); + }); + } + + + @Test + void shouldConvertEnhancedDocumentToDocumentEntity() { + + var enhancedDocument = EnhancedDocument.builder() + .json(""" + { + "%s":"Max", + "%s": "person", + "name":"Maximillian", + "number": 123, + "address": { + "street": "Rua tralala" + }, + "phones": [ + "1111-2222", + "2222-3333" + ] + } + """.formatted(DocumentEntityConverter.ID, DocumentEntityConverter.ENTITY)).build(); + var expected = DocumentEntityConverter.toDocumentEntity(entityNameResolver, enhancedDocument); + + assertSoftly(softly -> { + softly.assertThat(expected).as("cannot return a null reference") + .isNotNull(); + softly.assertThat(expected.name()).as("documentEntity name is not correct") + .isEqualTo("person"); + + softly.assertThat(expected.find("_id", String.class)) + .as("documentEntity._id was parsed incorrectly") + .hasValue("Max"); + softly.assertThat(expected.find("name", String.class)) + .as("documentEntity.name was parsed incorrectly") + .hasValue("Maximillian"); + softly.assertThat(expected.find("number", Integer.class)) + .as("documentEntity.number was parsed incorrectly") + .hasValue(123); + softly.assertThat(expected.find("address", new TypeReference>() { + })) + .as("documentEntity.address was parsed incorrectly") + .contains(List.of(Document.of("street", "Rua tralala"))); + softly.assertThat(expected.find("phones", new TypeReference>() { + })) + .as("documentEntity.phones was parsed incorrectly") + .contains(List.of("1111-2222", "2222-3333")); + + }); + } + + +} diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverterTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverterTest.java deleted file mode 100644 index 26cb27278..000000000 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentEntityConverterTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2023 Contributors to the Eclipse Foundation - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Apache License v2.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. - * - * You may elect to redistribute this code under either of these licenses. - * - * Contributors: - * - * Maximillian Arruda - */ - -package org.eclipse.jnosql.databases.dynamodb.communication; - -import jakarta.json.Json; -import jakarta.json.bind.Jsonb; -import org.eclipse.jnosql.communication.driver.JsonbSupplier; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfSystemProperty; - -import java.io.StringReader; - -import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; -import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; - -@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) -class DynamoDBDocumentEntityConverterTest { - - private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); - - - @Test - void shouldConvertDocumentEntityToEnhancedDocument() { - - assertSoftly(softly -> { - var entity = DocumentEntityGenerator.getEntity(); - var enhancedDocument = DynamoDBDocumentEntityConverter.toEnhancedDocument(entity); - var expected = Json.createReader(new StringReader(JSONB.toJson(DynamoDBDocumentEntityConverter.getMap(entity)))).readObject(); - var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); - softly.assertThat(actual).as("cannot convert a simple DocumentEntity") - .isEqualTo(expected); - }); - } - - @Test - void shouldConvertDocumentEntityWithSubDocumentsToEnhancedDocument() { - - assertSoftly(softly -> { - var entity = DocumentEntityGenerator.getEntityWithSubDocuments(3); - var enhancedDocument = DynamoDBDocumentEntityConverter.toEnhancedDocument(entity); - var expected = Json.createReader(new StringReader(JSONB.toJson(DynamoDBDocumentEntityConverter.getMap(entity)))).readObject(); - var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); - softly.assertThat(actual).as("cannot convert a DocumentEntity with document sublist") - .isEqualTo(expected); - }); - } - - -} From c71eb7370084e397d65f48afa0e69e566ccb236d Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:41:53 -0300 Subject: [PATCH 19/70] refactor: format and rename method in classes Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DocumentEntityGenerator.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java index 5caaf34fb..7dd2bbe1d 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java @@ -32,9 +32,8 @@ final class DocumentEntityGenerator { static final String COLLECTION_NAME = "music"; - static DocumentEntity getEntity() { - return getEntityWithSubDocuments(0); + return getEntityWithSubDocuments(0); } static DocumentEntity getEntityWithSubDocuments(int levels) { @@ -47,7 +46,7 @@ static DocumentEntity getEntityWithSubDocuments(int levels) { map.put("float", 1f); map.put("bigdecimal", BigDecimal.valueOf(10.10)); map.put("city", "Salvador"); - map.put("texts",List.of("A","B","C")); + map.put("texts", List.of("A", "B", "C")); if (levels > 0) { addSubDocument(m -> map.put("level" + levels, m), levels - 1); } From 62065707c6c757fb4bca931bc7915203a078710c Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:42:22 -0300 Subject: [PATCH 20/70] chore: add properties for DynamoDB Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DynamoDBConfigurations.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConfigurations.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConfigurations.java index 0cf8275f7..34d03ec8c 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConfigurations.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConfigurations.java @@ -22,7 +22,9 @@ public enum DynamoDBConfigurations implements Supplier { REGION("jnosql.dynamodb.region"), PROFILE("jnosql.dynamodb.profile"), AWS_ACCESSKEY("jnosql.dynamodb.awsaccesskey"), - AWS_SECRET_ACCESS("jnosql.dynamodb.secretaccess"); + AWS_SECRET_ACCESS("jnosql.dynamodb.secretaccess"), + ENTITY_PARTITION_KEY("jnosql.dynamodb.entity.pk"), + CREATE_TABLES("jnosql.dynamodb.create.tables"); private final String configuration; From 6969a61ab90eed04e1da7345c2db44a7701b1b9f Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:43:39 -0300 Subject: [PATCH 21/70] chore: passing settings to implementation Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBDocumentConfiguration.java | 2 +- .../communication/DynamoDBDocumentManagerFactory.java | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java index 6612aefde..618838e0c 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java @@ -23,6 +23,6 @@ public class DynamoDBDocumentConfiguration extends DynamoDBConfiguration @Override public DynamoDBDocumentManagerFactory apply(Settings settings) { var dynamoDB = getDynamoDB(settings); - return new DynamoDBDocumentManagerFactory(dynamoDB); + return new DynamoDBDocumentManagerFactory(dynamoDB, settings); } } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java index e132c3d28..d16c988ef 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java @@ -15,6 +15,7 @@ package org.eclipse.jnosql.databases.dynamodb.communication; +import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.communication.document.DocumentManagerFactory; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -24,14 +25,16 @@ public class DynamoDBDocumentManagerFactory implements DocumentManagerFactory { private final DynamoDbClient dynamoDB; + private final Settings settings; - public DynamoDBDocumentManagerFactory(DynamoDbClient dynamoDB) { + public DynamoDBDocumentManagerFactory(DynamoDbClient dynamoDB, Settings settings) { this.dynamoDB = dynamoDB; + this.settings = settings; } @Override public DocumentManager apply(String database) { - return new DynamoDBDocumentManager(database, dynamoDB); + return new DynamoDBDocumentManager(database, dynamoDB, settings); } @Override From 599ccd5b82e733825607d3c4626fc4a696e2a770 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:44:46 -0300 Subject: [PATCH 22/70] test: rename enum and add utility methods Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBTestUtils.java | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index b446b3edf..f208bbcf8 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -16,6 +16,8 @@ import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.communication.keyvalue.BucketManagerFactory; +import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; +import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; @@ -31,33 +33,54 @@ enum DynamoDBTestUtils { - INSTANCE; + CONFIG; private final GenericContainer dynamodb = new GenericContainer("amazon/dynamodb-local:latest") - .withExposedPorts(8000) .waitingFor(Wait.defaultWaitStrategy()); BucketManagerFactory getBucketManagerFactory() { Settings settings = getSettings(); + return getBucketManagerFactory(settings); + } + + private static DynamoDBBucketManagerFactory getBucketManagerFactory(Settings settings) { DynamoDBKeyValueConfiguration configuration = new DynamoDBKeyValueConfiguration(); return configuration.apply(settings); } DynamoDBDocumentManagerFactory getDocumentManagerFactory() { Settings settings = getSettings(); + return getDocumentManagerFactory(settings); + } + + DynamoDBDocumentManagerFactory getDocumentManagerFactory(Settings settings) { var configuration = new DynamoDBDocumentConfiguration(); return configuration.apply(settings); } Settings getSettings() { dynamodb.start(); + String dynamoDBHost = getDynamoDBHost(dynamodb.getHost(), dynamodb.getFirstMappedPort()); + return getSettings(dynamoDBHost); + } + + @NotNull + Settings getSettings(String dynamoDBHost) { return Settings.builder() - .put(DynamoDBConfigurations.ENDPOINT, "http://" + dynamodb.getHost() + ":" + dynamodb.getFirstMappedPort()) - .put(DynamoDBConfigurations.AWS_ACCESSKEY, System.getProperty("AWS_ACCESS_KEY_ID", "DUMMYIDEXAMPLE")) - .put(DynamoDBConfigurations.AWS_SECRET_ACCESS, System.getProperty("AWS_SECRET_ACCESS_KEY", "DUMMYEXAMPLEKEY")) + .put(MappingConfigurations.DOCUMENT_DATABASE,"test") + .put(DynamoDBConfigurations.ENDPOINT, dynamoDBHost) + .put(DynamoDBConfigurations.AWS_ACCESSKEY, System.getProperty("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE")) + .put(DynamoDBConfigurations.AWS_SECRET_ACCESS, System.getProperty("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")) .put(DynamoDBConfigurations.PROFILE, System.getProperty("AWS_PROFILE", "default")) - .put(DynamoDBConfigurations.REGION, "us-west-2").build(); + .put(DynamoDBConfigurations.REGION, "us-west-2") + .put(DynamoDBConfigurations.ENTITY_PARTITION_KEY, "entityType") + .build(); + } + + @NotNull + String getDynamoDBHost(String host, int port) { + return "http://" + host + ":" + port; } void shutDown() { @@ -66,6 +89,10 @@ void shutDown() { DynamoDbClient getDynamoDbClient() { var settings = getSettings(); + return getDynamoDbClient(settings); + } + + DynamoDbClient getDynamoDbClient(Settings settings) { DynamoDBBuilderSync builderSync = new DynamoDBBuilderSync(); settings.get(DynamoDBConfigurations.ENDPOINT, String.class).ifPresent(builderSync::endpoint); settings.get(DynamoDBConfigurations.AWS_ACCESSKEY, String.class).ifPresent(builderSync::awsAccessKey); @@ -76,7 +103,12 @@ DynamoDbClient getDynamoDbClient() { } DynamoDbEnhancedClient getDynamoDbEnhancedClient() { - return DynamoDbEnhancedClient.builder().dynamoDbClient(getDynamoDbClient()).build(); + DynamoDbClient dynamoDbClient = getDynamoDbClient(); + return getDynamoDbEnhancedClient(dynamoDbClient); + } + + DynamoDbEnhancedClient getDynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) { + return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build(); } public static DescribeTableEnhancedResponse createTable(DynamoDbEnhancedClient enhancedClient, String tableName, Function documentTableSchema) { From 3ab9d4d8018fb9c6a19e151cf44af8245abe7d68 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:45:18 -0300 Subject: [PATCH 23/70] test: using renamed enum from utility class Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBDocumentConfigurationTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java index fe87bd8fa..af7fe0353 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java @@ -15,7 +15,6 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.communication.document.DocumentConfiguration; import org.eclipse.jnosql.communication.document.DocumentManagerFactory; import org.junit.jupiter.api.Assertions; @@ -48,7 +47,7 @@ void shouldReturnDocumentManagerFactory() { var configuration = DocumentConfiguration .getConfiguration(DynamoDBDocumentConfiguration.class); - var settings = DynamoDBTestUtils.INSTANCE.getSettings(); + var settings = DynamoDBTestUtils.CONFIG.getSettings(); assertSoftly(softly -> { softly.assertThat(configuration) From 7d4c6de3718920f5c11adeb86e137630aad8bbb2 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:45:40 -0300 Subject: [PATCH 24/70] test: using renamed enum from utility class Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBDocumentManagerFactoryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java index 7dafdf666..d4c874361 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java @@ -31,7 +31,7 @@ class DynamoDBDocumentManagerFactoryTest { @BeforeEach void setup() { - this.documentManagerFactory = DynamoDBTestUtils.INSTANCE.getDocumentManagerFactory(); + this.documentManagerFactory = DynamoDBTestUtils.CONFIG.getDocumentManagerFactory(); assertSoftly(softly -> { softly.assertThat(documentManagerFactory).isNotNull(); softly.assertThat(documentManagerFactory).isInstanceOf(DynamoDBDocumentManagerFactory.class); From 0be7630dac70f7a61c6908401314b7d36620e7e0 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:45:59 -0300 Subject: [PATCH 25/70] test: using renamed enum from utility class Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBKeyValueEntityManagerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBKeyValueEntityManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBKeyValueEntityManagerTest.java index 94fc7a572..922601982 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBKeyValueEntityManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBKeyValueEntityManagerTest.java @@ -54,7 +54,7 @@ class DynamoDBKeyValueEntityManagerTest { @BeforeEach void init() { - keyValueEntityManagerFactory = DynamoDBTestUtils.INSTANCE.getBucketManagerFactory(); + keyValueEntityManagerFactory = DynamoDBTestUtils.CONFIG.getBucketManagerFactory(); keyValueEntityManager = keyValueEntityManagerFactory.apply("users-entity"); } @@ -118,7 +118,7 @@ void shouldRemoveMultiKey() { @AfterAll static void shutDown() { - DynamoDBTestUtils.INSTANCE.shutDown(); + DynamoDBTestUtils.CONFIG.shutDown(); } } From 949d3fdbdc7cff0f423c296ce1dda18b73ce263d Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:47:29 -0300 Subject: [PATCH 26/70] chore: implement and test documentManager.insert() method implementation Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManager.java | 136 ++++++++++++++++-- .../DynamoDBDocumentManagerTest.java | 103 ++++++++++++- 2 files changed, 228 insertions(+), 11 deletions(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java index 1a789dd9a..a524aa0c5 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -15,54 +15,174 @@ package org.eclipse.jnosql.databases.dynamodb.communication; +import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.communication.document.DocumentPreparedStatement; import org.eclipse.jnosql.communication.document.DocumentQuery; +import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider; +import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.TableMetadata; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveRequest; +import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveResponse; +import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; +import software.amazon.awssdk.services.dynamodb.model.TimeToLiveStatus; import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static java.util.Objects.requireNonNull; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toEnhancedDocument; public class DynamoDBDocumentManager implements DocumentManager { private final String database; + + private final Settings settings; + private final DynamoDbClient dynamoDbClient; - public DynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient) { + private final DynamoDbEnhancedClient dynamoDbEnhancedClient; + + private final UnaryOperator entityNameResolver; + + private final ConcurrentHashMap> tables = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> ttlAttributeNamesByTable = new ConcurrentHashMap<>(); + + public DynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient, Settings settings) { this.database = database; this.dynamoDbClient = dynamoDbClient; + this.dynamoDbEnhancedClient = DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build(); + this.settings = settings; + this.entityNameResolver = this::resolveEntityName; + } + + private String resolveEntityName(String entityName) { + return this.settings.get(DynamoDBConfigurations.ENTITY_PARTITION_KEY, String.class).orElse(entityName); } public DynamoDbClient getDynamoDbClient() { return dynamoDbClient; } + public DynamoDbEnhancedClient getDynamoDbEnhancedClient() { + return dynamoDbEnhancedClient; + } + @Override public String name() { return database; } + public UnaryOperator getEntityNameResolver() { + return this.entityNameResolver; + } + @Override public DocumentEntity insert(DocumentEntity documentEntity) { - return null; + requireNonNull(documentEntity, "documentEntity is required"); + var enhancedDocument = convertToEnhancedDocument(documentEntity); + getTableFor(documentEntity.name()) + .putItem(enhancedDocument); + return documentEntity; + } + + private EnhancedDocument convertToEnhancedDocument(DocumentEntity documentEntity) { + return toEnhancedDocument(getEntityNameResolver(), documentEntity); + } + + private DynamoDbTable getTableFor(String name) { + return this.tables.computeIfAbsent(name, this::getOrCreateTable); + } + + private Supplier getTTLAttributeNameFor(String name) { + return this.ttlAttributeNamesByTable.computeIfAbsent(name, this::getOrCreateTableWithTTLSupport); + } + + private Supplier getOrCreateTableWithTTLSupport(String nameKey) { + DynamoDbTable table = this.getOrCreateTable(nameKey); + DescribeTimeToLiveResponse describeTimeToLiveResponse = getDynamoDbClient().describeTimeToLive(DescribeTimeToLiveRequest.builder() + .tableName(table.tableName()).build()); + if (TimeToLiveStatus.ENABLED.equals(describeTimeToLiveResponse.timeToLiveDescription().timeToLiveStatus())) { + var ttlAttributeName = describeTimeToLiveResponse.timeToLiveDescription().attributeName(); + return () -> ttlAttributeName; + } + return unsupportedTTL(table.tableName()); + } + + private Supplier unsupportedTTL(String tableName) { + return () -> tableName + " don't support TTL operations. Check if TTL support is enabled for this table."; + } + + private DynamoDbTable getOrCreateTable(String nameKey) { + DynamoDbTable table = dynamoDbEnhancedClient + .table(nameKey, TableSchema.documentSchemaBuilder() + .addIndexPartitionKey(TableMetadata.primaryIndexName(), getEntityFieldName(), AttributeValueType.S) + .addIndexSortKey(TableMetadata.primaryIndexName(), DocumentEntityConverter.ID, AttributeValueType.S) + .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) + .build()); + try { + table.describeTable(); + return table; + } catch (ResourceNotFoundException ex) { + if (shouldCreateTables()) { + table.createTable(); + return table; + } + throw ex; + } + } + + private boolean shouldCreateTables() { + return this.settings + .get(DynamoDBConfigurations.CREATE_TABLES, Boolean.class) + .orElse(false); + } + + private String getEntityFieldName() { + return getEntityNameResolver().apply(DocumentEntityConverter.ENTITY); } @Override - public DocumentEntity insert(DocumentEntity documentEntity, Duration duration) { - return null; + public DocumentEntity insert(DocumentEntity documentEntity, Duration ttl) { + requireNonNull(documentEntity, "documentEntity is required"); + requireNonNull(ttl, "ttl is required"); + DynamoDbTable tableFor = getTableFor(documentEntity.name()); + documentEntity.add(getTTLAttributeNameFor(tableFor.tableName()).get(), Instant.now().plus(ttl).truncatedTo(ChronoUnit.SECONDS)); + var enhancedDocument = convertToEnhancedDocument(documentEntity); + tableFor.putItem(enhancedDocument); + return documentEntity; } @Override - public Iterable insert(Iterable iterable) { - return null; + public Iterable insert(Iterable entities) { + requireNonNull(entities, "entities is required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(this::insert) + .collect(Collectors.toList()); } @Override - public Iterable insert(Iterable iterable, Duration duration) { - return null; + public Iterable insert(Iterable entities, Duration ttl) { + requireNonNull(entities, "entities is required"); + requireNonNull(ttl, "ttl is required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(e -> this.insert(e, ttl)) + .collect(Collectors.toList()); } @Override diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java index e51ecda92..e03253346 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java @@ -17,28 +17,125 @@ import org.assertj.core.api.Assertions; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; +import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; +import software.amazon.awssdk.enhanced.dynamodb.*; +import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; +import software.amazon.awssdk.enhanced.dynamodb.model.DescribeTableEnhancedResponse; +import software.amazon.awssdk.enhanced.dynamodb.model.QueryConditional; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; +import java.time.Duration; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils.CONFIG; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils.createTable; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) class DynamoDBDocumentManagerTest { private DocumentManager documentManager; + private DynamoDbClient dynamoDbClient; + + private DynamoDbEnhancedClient dynamoDbEnhancedClient; + + private DynamoDbTable table; + + private String database; + private UnaryOperator entityNameResolver; + private boolean tableWasCreated; + @BeforeEach void setUp() { - documentManager = DynamoDBTestUtils.INSTANCE.getDocumentManagerFactory().apply("default"); + var settings = CONFIG.getSettings(CONFIG.getDynamoDBHost("localhost", 8000)); + database = settings.get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); + entityNameResolver = entityName -> settings.get(DynamoDBConfigurations.ENTITY_PARTITION_KEY, String.class).orElse(entityName); + + var documentManagerFactory = CONFIG.getDocumentManagerFactory(settings); + documentManager = documentManagerFactory.apply(database); Assertions.assertThat(documentManager).isNotNull(); + + dynamoDbClient = CONFIG.getDynamoDbClient(settings); + dynamoDbEnhancedClient = CONFIG.getDynamoDbEnhancedClient(dynamoDbClient); + table = dynamoDbEnhancedClient.table("music", + TableSchema.documentSchemaBuilder() + .addIndexPartitionKey(TableMetadata.primaryIndexName(), entityNameResolver.apply("@entity"), AttributeValueType.S) + .addIndexSortKey(TableMetadata.primaryIndexName(), "_id", AttributeValueType.S) + .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) + .build()); + + try { + table.describeTable(); + } catch (ResourceNotFoundException ex) { + table.createTable(); + tableWasCreated = true; + } + } + + @AfterEach + void tearDown() { + if (tableWasCreated) + table.deleteTable(); } @Test - void shouldReturnName(){ - Assertions.assertThat(documentManager.name()).isEqualTo("default"); + void shouldReturnName() { + Assertions.assertThat(documentManager.name()).isEqualTo(database); } + @Test + void shouldReturnErrorWhenInsertNull() { + assertSoftly(softly -> { + softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null)) + .as("should return error when insert a null DocumentEntity reference") + .isExactlyInstanceOf(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, Duration.ofSeconds(1))) + .as("should return error when insert a null DocumentEntity reference with TTL param") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, null)) + .as("should return error when insert a null DocumentEntity reference with nullable TTL param") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null)) + .as("should return error when insert a null Iterable reference") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null, Duration.ofSeconds(1))) + .as("should return error when insert a null Iterable reference with TTL param") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null, null)) + .as("should return error when insert a null Iterable reference with nullable TTL param") + .isInstanceOfAny(NullPointerException.class); + }); + } + + @Test + void shouldInsertDocumentWithNoSubDocuments() { + + DocumentEntity entity = DocumentEntityGenerator.getEntityWithSubDocuments(0); + var _entityType = entity.name(); + var id = entity.find("_id", String.class).orElseThrow(); + var persistedEntity = documentManager.insert(entity); + assertSoftly(softly -> { + softly.assertThat(persistedEntity).as("should return the persistent document entity from documentManager.insert() method").isNotNull(); + EnhancedDocument persistedItem = table.getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); + softly.assertThat(persistedItem).as("should return the item from dynamodb").isNotNull(); + }); + + + } + + } From 54d7fef028a7c17c11474f3863f3650a6223aa3f Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 03:50:25 -0300 Subject: [PATCH 27/70] chore: removed unnecessary import Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DocumentEntityConverter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java index 29339c2b5..e5c1a18e3 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java @@ -21,7 +21,6 @@ import org.eclipse.jnosql.communication.driver.JsonbSupplier; import org.eclipse.jnosql.communication.driver.ValueUtil; import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; From de89fddda530c3a3c0874b8ca26d72bf495f11aa Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 04:14:29 -0300 Subject: [PATCH 28/70] refactor: rename and removed unnecessary methods Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManager.java | 88 ++++++------------- 1 file changed, 29 insertions(+), 59 deletions(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java index a524aa0c5..c5b9ad306 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -19,14 +19,8 @@ import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; -import org.eclipse.jnosql.communication.document.DocumentPreparedStatement; import org.eclipse.jnosql.communication.document.DocumentQuery; -import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider; -import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; -import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; -import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; -import software.amazon.awssdk.enhanced.dynamodb.TableMetadata; -import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.enhanced.dynamodb.*; import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveRequest; @@ -37,11 +31,9 @@ import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -58,20 +50,20 @@ public class DynamoDBDocumentManager implements DocumentManager { private final DynamoDbEnhancedClient dynamoDbEnhancedClient; - private final UnaryOperator entityNameResolver; + private final UnaryOperator entityNameAttributeNameResolver; private final ConcurrentHashMap> tables = new ConcurrentHashMap<>(); private final ConcurrentHashMap> ttlAttributeNamesByTable = new ConcurrentHashMap<>(); public DynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient, Settings settings) { + this.settings = settings; this.database = database; this.dynamoDbClient = dynamoDbClient; this.dynamoDbEnhancedClient = DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build(); - this.settings = settings; - this.entityNameResolver = this::resolveEntityName; + this.entityNameAttributeNameResolver = this::resolveEntityNameAttributeName; } - private String resolveEntityName(String entityName) { + private String resolveEntityNameAttributeName(String entityName) { return this.settings.get(DynamoDBConfigurations.ENTITY_PARTITION_KEY, String.class).orElse(entityName); } @@ -88,8 +80,8 @@ public String name() { return database; } - public UnaryOperator getEntityNameResolver() { - return this.entityNameResolver; + UnaryOperator entityNameAttributeNameResolver() { + return this.entityNameAttributeNameResolver; } @Override @@ -102,36 +94,36 @@ public DocumentEntity insert(DocumentEntity documentEntity) { } private EnhancedDocument convertToEnhancedDocument(DocumentEntity documentEntity) { - return toEnhancedDocument(getEntityNameResolver(), documentEntity); + return toEnhancedDocument(entityNameAttributeNameResolver(), documentEntity); } private DynamoDbTable getTableFor(String name) { return this.tables.computeIfAbsent(name, this::getOrCreateTable); } - private Supplier getTTLAttributeNameFor(String name) { - return this.ttlAttributeNamesByTable.computeIfAbsent(name, this::getOrCreateTableWithTTLSupport); + private Supplier getTTLAttributeNameFor(String tableName) { + return this.ttlAttributeNamesByTable.computeIfAbsent(tableName, this::getTTLAttributeNameSupplierFromTable); } - private Supplier getOrCreateTableWithTTLSupport(String nameKey) { - DynamoDbTable table = this.getOrCreateTable(nameKey); + private Supplier getTTLAttributeNameSupplierFromTable(String tableName) { + DynamoDbTable table = this.getOrCreateTable(tableName); DescribeTimeToLiveResponse describeTimeToLiveResponse = getDynamoDbClient().describeTimeToLive(DescribeTimeToLiveRequest.builder() .tableName(table.tableName()).build()); if (TimeToLiveStatus.ENABLED.equals(describeTimeToLiveResponse.timeToLiveDescription().timeToLiveStatus())) { var ttlAttributeName = describeTimeToLiveResponse.timeToLiveDescription().attributeName(); return () -> ttlAttributeName; } - return unsupportedTTL(table.tableName()); + return unsupportedTTLSupplierFor(table.tableName()); } - private Supplier unsupportedTTL(String tableName) { + private Supplier unsupportedTTLSupplierFor(String tableName) { return () -> tableName + " don't support TTL operations. Check if TTL support is enabled for this table."; } private DynamoDbTable getOrCreateTable(String nameKey) { DynamoDbTable table = dynamoDbEnhancedClient .table(nameKey, TableSchema.documentSchemaBuilder() - .addIndexPartitionKey(TableMetadata.primaryIndexName(), getEntityFieldName(), AttributeValueType.S) + .addIndexPartitionKey(TableMetadata.primaryIndexName(), getEntityNameAttributeName(), AttributeValueType.S) .addIndexSortKey(TableMetadata.primaryIndexName(), DocumentEntityConverter.ID, AttributeValueType.S) .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) .build()); @@ -153,8 +145,8 @@ private boolean shouldCreateTables() { .orElse(false); } - private String getEntityFieldName() { - return getEntityNameResolver().apply(DocumentEntityConverter.ENTITY); + private String getEntityNameAttributeName() { + return entityNameAttributeNameResolver().apply(DocumentEntityConverter.ENTITY); } @Override @@ -173,7 +165,7 @@ public Iterable insert(Iterable entities) { requireNonNull(entities, "entities is required"); return StreamSupport.stream(entities.spliterator(), false) .map(this::insert) - .collect(Collectors.toList()); + .toList(); } @Override @@ -182,57 +174,35 @@ public Iterable insert(Iterable entities, Durati requireNonNull(ttl, "ttl is required"); return StreamSupport.stream(entities.spliterator(), false) .map(e -> this.insert(e, ttl)) - .collect(Collectors.toList()); + .toList(); } @Override public DocumentEntity update(DocumentEntity documentEntity) { - return null; + throw new UnsupportedOperationException("update method must be implemented!"); } @Override - public Iterable update(Iterable iterable) { - return null; + public Iterable update(Iterable entities) { + requireNonNull(entities, "entities is required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(this::update) + .toList(); } @Override public void delete(DocumentDeleteQuery documentDeleteQuery) { - + throw new UnsupportedOperationException("delete method must be implemented!"); } @Override public Stream select(DocumentQuery documentQuery) { - return null; - } - - @Override - public long count(DocumentQuery query) { - return DocumentManager.super.count(query); - } - - @Override - public boolean exists(DocumentQuery query) { - return DocumentManager.super.exists(query); - } - - @Override - public Stream query(String query) { - return DocumentManager.super.query(query); - } - - @Override - public DocumentPreparedStatement prepare(String query) { - return DocumentManager.super.prepare(query); - } - - @Override - public Optional singleResult(DocumentQuery query) { - return DocumentManager.super.singleResult(query); + throw new UnsupportedOperationException("select method must be implemented!"); } @Override - public long count(String s) { - return 0; + public long count(String tableName) { + throw new UnsupportedOperationException("count method must be implemented!"); } @Override From e181422b579c3e4fcc5499066833d4139bd8fcdb Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Fri, 5 Jan 2024 04:23:07 -0300 Subject: [PATCH 29/70] refactor: fixed checkstyle issues Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DynamoDBDocumentManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java index c5b9ad306..88d57278d 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -20,7 +20,12 @@ import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.communication.document.DocumentQuery; -import software.amazon.awssdk.enhanced.dynamodb.*; +import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider; +import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.TableMetadata; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveRequest; From ca844dc8a7ca5b44533e31a131187a8b5da7c6ae Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 16:34:13 -0300 Subject: [PATCH 30/70] test: add DataFaker to help with tests Signed-off-by: Maximillian Arruda --- jnosql-dynamodb/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jnosql-dynamodb/pom.xml b/jnosql-dynamodb/pom.xml index 1df55fea3..73a82aee2 100644 --- a/jnosql-dynamodb/pom.xml +++ b/jnosql-dynamodb/pom.xml @@ -53,6 +53,12 @@ dynamodb-enhanced ${dynamodb.version} + + net.datafaker + datafaker + 2.0.2 + test + org.testcontainers testcontainers From 50af515bb00d2a1736f360f1006b0e9f4924b965 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 16:35:46 -0300 Subject: [PATCH 31/70] test: change to instantiate a GenericContainer Signed-off-by: Maximillian Arruda --- .../databases/dynamodb/communication/DynamoDBTestUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index f208bbcf8..c4f38bc3a 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -37,6 +37,8 @@ enum DynamoDBTestUtils { private final GenericContainer dynamodb = new GenericContainer("amazon/dynamodb-local:latest") + .withReuse(true) + .withExposedPorts(8000) .waitingFor(Wait.defaultWaitStrategy()); BucketManagerFactory getBucketManagerFactory() { From 129f7db61760893d12259dc8bd40eabb57b8cb32 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 16:36:17 -0300 Subject: [PATCH 32/70] test: rename methods and using DataFaker Signed-off-by: Maximillian Arruda --- .../DocumentEntityGenerator.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java index 7dd2bbe1d..1269e26e2 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java @@ -15,6 +15,7 @@ package org.eclipse.jnosql.databases.dynamodb.communication; +import net.datafaker.Faker; import org.eclipse.jnosql.communication.document.Document; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.Documents; @@ -31,21 +32,23 @@ final class DocumentEntityGenerator { static final String COLLECTION_NAME = "music"; + static final Faker faker = new Faker(); - static DocumentEntity getEntity() { - return getEntityWithSubDocuments(0); + static DocumentEntity createRandomEntity() { + return createRandomEntityWithSubDocuments(0); } - static DocumentEntity getEntityWithSubDocuments(int levels) { + static DocumentEntity createRandomEntityWithSubDocuments(int levels) { Map map = new HashMap<>(); map.put("_id", UUID.randomUUID().toString()); - map.put("name", "Poliana"); + map.put("name", faker.name().firstName()); map.put("hoje", LocalDate.now()); map.put("agora", LocalDateTime.now()); - map.put("integer", 1); - map.put("float", 1f); + map.put("integerNumber", faker.random().nextInt(1, 10)); + map.put("floatNumber", (float) faker.random().nextDouble(1.0, 10.0)); + map.put("doubleNumber", faker.random().nextDouble(1.0, 10.0)); map.put("bigdecimal", BigDecimal.valueOf(10.10)); - map.put("city", "Salvador"); + map.put("city", faker.address().city()); map.put("texts", List.of("A", "B", "C")); if (levels > 0) { addSubDocument(m -> map.put("level" + levels, m), levels - 1); @@ -62,8 +65,9 @@ static void addSubDocument(Consumer> owner, int level) { map.put("text", UUID.randomUUID().toString()); map.put("hoje", LocalDate.now()); map.put("agora", LocalDateTime.now()); - map.put("integer", 1); - map.put("float", 1f); + map.put("integerNumber", faker.random().nextInt(1, 10)); + map.put("floatNumber", (float) faker.random().nextDouble(1.0, 10.0)); + map.put("doubleNumber", faker.random().nextDouble(1.0, 10.0)); map.put("bigdecimal", BigDecimal.valueOf(10.10)); if (level > 0) { addSubDocument(m -> map.put("level" + level, m), level - 1); From 92951530f94c0eef6d3fb673d7cfabe368883613 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 16:38:30 -0300 Subject: [PATCH 33/70] test: refactor test codes Signed-off-by: Maximillian Arruda --- .../DocumentEntityConverterTest.java | 4 +- .../DynamoDBDocumentManagerTest.java | 70 +++++++++++++++---- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java index 725b083fe..1f53a6672 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java @@ -43,7 +43,7 @@ class DocumentEntityConverterTest { void shouldConvertDocumentEntityToEnhancedDocument() { assertSoftly(softly -> { - var entity = DocumentEntityGenerator.getEntity(); + var entity = DocumentEntityGenerator.createRandomEntity(); var enhancedDocument = DocumentEntityConverter.toEnhancedDocument(entityNameResolver, entity); var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); @@ -56,7 +56,7 @@ void shouldConvertDocumentEntityToEnhancedDocument() { void shouldConvertDocumentEntityWithSubDocumentsToEnhancedDocument() { assertSoftly(softly -> { - var entity = DocumentEntityGenerator.getEntityWithSubDocuments(3); + var entity = DocumentEntityGenerator.createRandomEntityWithSubDocuments(3); var enhancedDocument = DocumentEntityConverter.toEnhancedDocument(entityNameResolver, entity); var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java index e03253346..6d90c11c7 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java @@ -17,8 +17,6 @@ import org.assertj.core.api.Assertions; -import org.assertj.core.api.SoftAssertions; -import org.eclipse.jnosql.communication.document.Document; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; @@ -28,21 +26,20 @@ import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import software.amazon.awssdk.enhanced.dynamodb.*; import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; -import software.amazon.awssdk.enhanced.dynamodb.model.DescribeTableEnhancedResponse; -import software.amazon.awssdk.enhanced.dynamodb.model.QueryConditional; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; import java.time.Duration; -import java.util.function.Consumer; +import java.util.List; import java.util.function.UnaryOperator; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityGenerator.createRandomEntity; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils.CONFIG; -import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils.createTable; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) class DynamoDBDocumentManagerTest { @@ -61,7 +58,7 @@ class DynamoDBDocumentManagerTest { @BeforeEach void setUp() { - var settings = CONFIG.getSettings(CONFIG.getDynamoDBHost("localhost", 8000)); + var settings = CONFIG.getSettings(); database = settings.get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); entityNameResolver = entityName -> settings.get(DynamoDBConfigurations.ENTITY_PARTITION_KEY, String.class).orElse(entityName); @@ -82,14 +79,18 @@ void setUp() { table.describeTable(); } catch (ResourceNotFoundException ex) { table.createTable(); - tableWasCreated = true; } } + @AfterEach void tearDown() { - if (tableWasCreated) - table.deleteTable(); + table.deleteTable(); + } + + private void cleanTable() { + table.deleteTable(); + table.createTable(); } @Test @@ -109,22 +110,65 @@ void shouldReturnErrorWhenInsertNull() { softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, null)) .as("should return error when insert a null DocumentEntity reference with nullable TTL param") .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert(DocumentEntityGenerator.createRandomEntity(), null)) + .as("should return error when insert a null DocumentEntity reference with nullable TTL param") + .isInstanceOfAny(NullPointerException.class); softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null)) .as("should return error when insert a null Iterable reference") .isInstanceOfAny(NullPointerException.class); softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null, Duration.ofSeconds(1))) .as("should return error when insert a null Iterable reference with TTL param") .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null, null)) + softly.assertThatThrownBy(() -> documentManager.insert(List.of(DocumentEntityGenerator.createRandomEntity()), null)) .as("should return error when insert a null Iterable reference with nullable TTL param") .isInstanceOfAny(NullPointerException.class); }); } @Test - void shouldInsertDocumentWithNoSubDocuments() { + void shouldInsert() { + + assertSoftly(softly -> { + DocumentEntity entity = createRandomEntity(); + var _entityType = entity.name(); + var id = entity.find("_id", String.class).orElseThrow(); + var persistedEntity = documentManager.insert(entity); + softly.assertThat(persistedEntity) + .as("documentManager.insert(DocumentEntity) method should return a non-null persistent DocumentEntity") + .isNotNull(); + + EnhancedDocument persistedItem = table.getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); + + softly.assertThat(persistedItem).as("should return the item from dynamodb").isNotNull(); + }); + + assertSoftly(softly -> { + var entities = List.of(createRandomEntity(),createRandomEntity(),createRandomEntity()); + Iterable persistedEntities = documentManager.insert(entities); + softly.assertThat(persistedEntities) + .as("documentManager.insert(Iterable<>) should returns the non-null list of DocumentEntity").isNotNull(); + + assertThat(persistedEntities) + .as("documentmanager.insert(iterable<>) should returns a corresponded list of DocumentEntity") + .hasSize(3); + + persistedEntities.forEach(entity -> { + var _entityType = entity.name(); + var id = entity.find("_id", String.class).orElseThrow(); + EnhancedDocument persistedItem = table.getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); + softly.assertThat(persistedItem) + .as("all items of the list of DocumentEntity should be stored on dynamodb database. the entity %s not found" + .formatted(id)) + .isNotNull(); + }); + }); + + } + + @Test + void shouldInserts() { - DocumentEntity entity = DocumentEntityGenerator.getEntityWithSubDocuments(0); + DocumentEntity entity = createRandomEntity(); var _entityType = entity.name(); var id = entity.find("_id", String.class).orElseThrow(); var persistedEntity = documentManager.insert(entity); From 6d5a6dc0b27b62a413a8d90929661ce67bab3a3b Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 19:40:32 -0300 Subject: [PATCH 34/70] test: improve DocumentEntityGenerator class Signed-off-by: Maximillian Arruda --- .../communication/DocumentEntityGenerator.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java index 1269e26e2..6405b3af3 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java @@ -19,6 +19,7 @@ import org.eclipse.jnosql.communication.document.Document; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.Documents; +import org.jetbrains.annotations.NotNull; import java.math.BigDecimal; import java.time.LocalDate; @@ -31,14 +32,23 @@ final class DocumentEntityGenerator { - static final String COLLECTION_NAME = "music"; + static final String COLLECTION_NAME = "entityA"; static final Faker faker = new Faker(); static DocumentEntity createRandomEntity() { return createRandomEntityWithSubDocuments(0); } + static DocumentEntity createRandomEntity(String collectionName) { + return createRandomEntityWithSubDocuments(collectionName,0); + } + static DocumentEntity createRandomEntityWithSubDocuments(int levels) { + return createRandomEntityWithSubDocuments(COLLECTION_NAME, levels); + } + + @NotNull + private static DocumentEntity createRandomEntityWithSubDocuments(String collectionName, int levels) { Map map = new HashMap<>(); map.put("_id", UUID.randomUUID().toString()); map.put("name", faker.name().firstName()); @@ -46,14 +56,14 @@ static DocumentEntity createRandomEntityWithSubDocuments(int levels) { map.put("agora", LocalDateTime.now()); map.put("integerNumber", faker.random().nextInt(1, 10)); map.put("floatNumber", (float) faker.random().nextDouble(1.0, 10.0)); - map.put("doubleNumber", faker.random().nextDouble(1.0, 10.0)); + map.put("doubleNumber", faker.random().nextDouble(1.0, 10.0)); map.put("bigdecimal", BigDecimal.valueOf(10.10)); map.put("city", faker.address().city()); map.put("texts", List.of("A", "B", "C")); if (levels > 0) { addSubDocument(m -> map.put("level" + levels, m), levels - 1); } - DocumentEntity entity = DocumentEntity.of(COLLECTION_NAME); + DocumentEntity entity = DocumentEntity.of(collectionName); List documents = Documents.of(map); documents.forEach(entity::add); return entity; @@ -67,7 +77,7 @@ static void addSubDocument(Consumer> owner, int level) { map.put("agora", LocalDateTime.now()); map.put("integerNumber", faker.random().nextInt(1, 10)); map.put("floatNumber", (float) faker.random().nextDouble(1.0, 10.0)); - map.put("doubleNumber", faker.random().nextDouble(1.0, 10.0)); + map.put("doubleNumber", faker.random().nextDouble(1.0, 10.0)); map.put("bigdecimal", BigDecimal.valueOf(10.10)); if (level > 0) { addSubDocument(m -> map.put("level" + level, m), level - 1); From 9eb8f4c56c2e4ae0e035acfdf387c161adc4cd9e Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 19:41:32 -0300 Subject: [PATCH 35/70] test: refactor DynamoDBDocumentManagerFactoryTest class Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBDocumentManagerFactoryTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java index d4c874361..70941cc42 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java @@ -16,6 +16,8 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.document.DocumentManagerFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; @@ -23,6 +25,7 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) class DynamoDBDocumentManagerFactoryTest { @@ -37,7 +40,10 @@ void setup() { softly.assertThat(documentManagerFactory).isInstanceOf(DynamoDBDocumentManagerFactory.class); }); } - + @AfterEach + void tearDown() { + assertDoesNotThrow(documentManagerFactory::close, "DocumentManagerFactory.close() should be not throw exceptions"); + } @Test void shouldCreateDocumentManager() { var documentManager = documentManagerFactory.apply("anydatabase"); From 0a42ba484dcf58cf891bc5493ce078c43e9f6cc0 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 19:42:07 -0300 Subject: [PATCH 36/70] test: refactor DynamoDBTestUtils class Signed-off-by: Maximillian Arruda --- .../communication/DynamoDBTestUtils.java | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index c4f38bc3a..f29d4929c 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -15,6 +15,8 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.SettingsBuilder; +import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.communication.keyvalue.BucketManagerFactory; import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; import org.jetbrains.annotations.NotNull; @@ -27,9 +29,10 @@ import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.enhanced.dynamodb.model.DescribeTableEnhancedResponse; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.*; +import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; import java.util.function.Function; +import java.util.function.UnaryOperator; enum DynamoDBTestUtils { @@ -61,23 +64,43 @@ DynamoDBDocumentManagerFactory getDocumentManagerFactory(Settings settings) { return configuration.apply(settings); } + DocumentManager getDocumentManager(Settings settings) { + var database = settings + .get(MappingConfigurations.DOCUMENT_DATABASE, String.class) + .orElseThrow(); + return getDocumentManagerFactory(settings).apply(database); + } + Settings getSettings() { dynamodb.start(); String dynamoDBHost = getDynamoDBHost(dynamodb.getHost(), dynamodb.getFirstMappedPort()); return getSettings(dynamoDBHost); } - @NotNull Settings getSettings(String dynamoDBHost) { + return getSettingsBuilder(builder -> builder + .put(DynamoDBConfigurations.ENDPOINT, dynamoDBHost)) + .build(); + } + + Settings customSetting(SettingsBuilder builder) { + var defaultSetting = getSettings(); + var customSetting = builder.build(); return Settings.builder() - .put(MappingConfigurations.DOCUMENT_DATABASE,"test") - .put(DynamoDBConfigurations.ENDPOINT, dynamoDBHost) + .putAll(defaultSetting.toMap()) + .putAll(customSetting.toMap()) + .build(); + } + + @NotNull + private static SettingsBuilder getSettingsBuilder(UnaryOperator builder) { + return builder.apply(Settings.builder()) + .put(MappingConfigurations.DOCUMENT_DATABASE, "test") .put(DynamoDBConfigurations.AWS_ACCESSKEY, System.getProperty("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE")) .put(DynamoDBConfigurations.AWS_SECRET_ACCESS, System.getProperty("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")) .put(DynamoDBConfigurations.PROFILE, System.getProperty("AWS_PROFILE", "default")) .put(DynamoDBConfigurations.REGION, "us-west-2") - .put(DynamoDBConfigurations.ENTITY_PARTITION_KEY, "entityType") - .build(); + .put(DynamoDBConfigurations.ENTITY_PARTITION_KEY, "entityType"); } @NotNull From c03128df616d8cb52d3064827cff789e646f1169 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 19:46:47 -0300 Subject: [PATCH 37/70] feat: add count(String) implementation and tests Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManager.java | 36 +-- .../DynamoDBDocumentManagerTest.java | 237 +++++++++++------- 2 files changed, 166 insertions(+), 107 deletions(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java index 88d57278d..785ade728 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -36,6 +36,7 @@ import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import java.util.function.UnaryOperator; @@ -93,7 +94,7 @@ UnaryOperator entityNameAttributeNameResolver() { public DocumentEntity insert(DocumentEntity documentEntity) { requireNonNull(documentEntity, "documentEntity is required"); var enhancedDocument = convertToEnhancedDocument(documentEntity); - getTableFor(documentEntity.name()) + createIfNeeded(getTableFor(documentEntity.name())) .putItem(enhancedDocument); return documentEntity; } @@ -103,7 +104,7 @@ private EnhancedDocument convertToEnhancedDocument(DocumentEntity documentEntity } private DynamoDbTable getTableFor(String name) { - return this.tables.computeIfAbsent(name, this::getOrCreateTable); + return this.tables.computeIfAbsent(name, this::buildTable); } private Supplier getTTLAttributeNameFor(String tableName) { @@ -111,7 +112,7 @@ private Supplier getTTLAttributeNameFor(String tableName) { } private Supplier getTTLAttributeNameSupplierFromTable(String tableName) { - DynamoDbTable table = this.getOrCreateTable(tableName); + DynamoDbTable table = buildTable(tableName); DescribeTimeToLiveResponse describeTimeToLiveResponse = getDynamoDbClient().describeTimeToLive(DescribeTimeToLiveRequest.builder() .tableName(table.tableName()).build()); if (TimeToLiveStatus.ENABLED.equals(describeTimeToLiveResponse.timeToLiveDescription().timeToLiveStatus())) { @@ -125,23 +126,24 @@ private Supplier unsupportedTTLSupplierFor(String tableName) { return () -> tableName + " don't support TTL operations. Check if TTL support is enabled for this table."; } - private DynamoDbTable getOrCreateTable(String nameKey) { - DynamoDbTable table = dynamoDbEnhancedClient + private DynamoDbTable buildTable(String nameKey) { + return dynamoDbEnhancedClient .table(nameKey, TableSchema.documentSchemaBuilder() .addIndexPartitionKey(TableMetadata.primaryIndexName(), getEntityNameAttributeName(), AttributeValueType.S) .addIndexSortKey(TableMetadata.primaryIndexName(), DocumentEntityConverter.ID, AttributeValueType.S) .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) .build()); - try { - table.describeTable(); - return table; - } catch (ResourceNotFoundException ex) { - if (shouldCreateTables()) { + } + + private DynamoDbTable createIfNeeded(DynamoDbTable table) { + if (shouldCreateTables()) { + try { + table.describeTable(); + } catch (ResourceNotFoundException ex) { table.createTable(); - return table; } - throw ex; } + return table; } private boolean shouldCreateTables() { @@ -158,7 +160,7 @@ private String getEntityNameAttributeName() { public DocumentEntity insert(DocumentEntity documentEntity, Duration ttl) { requireNonNull(documentEntity, "documentEntity is required"); requireNonNull(ttl, "ttl is required"); - DynamoDbTable tableFor = getTableFor(documentEntity.name()); + DynamoDbTable tableFor = createIfNeeded(getTableFor(documentEntity.name())); documentEntity.add(getTTLAttributeNameFor(tableFor.tableName()).get(), Instant.now().plus(ttl).truncatedTo(ChronoUnit.SECONDS)); var enhancedDocument = convertToEnhancedDocument(documentEntity); tableFor.putItem(enhancedDocument); @@ -207,7 +209,13 @@ public Stream select(DocumentQuery documentQuery) { @Override public long count(String tableName) { - throw new UnsupportedOperationException("count method must be implemented!"); + Objects.requireNonNull(tableName, "tableName is required"); + DynamoDbTable table = getTableFor(tableName); + try { + return table.describeTable().table().itemCount(); + } catch (ResourceNotFoundException ex) { + return 0; + } } @Override diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java index 6d90c11c7..dfb2216d0 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java @@ -17,6 +17,8 @@ import org.assertj.core.api.Assertions; +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.SettingsBuilder; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; @@ -24,17 +26,26 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; -import software.amazon.awssdk.enhanced.dynamodb.*; +import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider; +import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.Key; +import software.amazon.awssdk.enhanced.dynamodb.TableMetadata; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; +import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; import java.time.Duration; import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.function.UnaryOperator; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; @@ -44,142 +55,182 @@ @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) class DynamoDBDocumentManagerTest { - private DocumentManager documentManager; private DynamoDbClient dynamoDbClient; private DynamoDbEnhancedClient dynamoDbEnhancedClient; - private DynamoDbTable table; - - private String database; private UnaryOperator entityNameResolver; - private boolean tableWasCreated; + @BeforeEach void setUp() { var settings = CONFIG.getSettings(); - database = settings.get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); entityNameResolver = entityName -> settings.get(DynamoDBConfigurations.ENTITY_PARTITION_KEY, String.class).orElse(entityName); - - var documentManagerFactory = CONFIG.getDocumentManagerFactory(settings); - documentManager = documentManagerFactory.apply(database); - Assertions.assertThat(documentManager).isNotNull(); - dynamoDbClient = CONFIG.getDynamoDbClient(settings); dynamoDbEnhancedClient = CONFIG.getDynamoDbEnhancedClient(dynamoDbClient); - table = dynamoDbEnhancedClient.table("music", - TableSchema.documentSchemaBuilder() - .addIndexPartitionKey(TableMetadata.primaryIndexName(), entityNameResolver.apply("@entity"), AttributeValueType.S) - .addIndexSortKey(TableMetadata.primaryIndexName(), "_id", AttributeValueType.S) - .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) - .build()); + } - try { - table.describeTable(); - } catch (ResourceNotFoundException ex) { - table.createTable(); - } + private DocumentManager getDocumentManagerCannotCreateTables() { + var settings = CONFIG.customSetting(Settings.builder() + .put(DynamoDBConfigurations.CREATE_TABLES, "false")); + var database = settings.get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); + var documentManagerFactory = CONFIG.getDocumentManagerFactory(settings); + return documentManagerFactory.apply(database); + } + + private DocumentManager getDocumentManagerCanCreateTables() { + var settings = CONFIG.customSetting(Settings.builder() + .put(DynamoDBConfigurations.CREATE_TABLES, "true")); + var database = settings.get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); + var documentManagerFactory = CONFIG.getDocumentManagerFactory(settings); + return documentManagerFactory.apply(database); } @AfterEach void tearDown() { - table.deleteTable(); - } - - private void cleanTable() { - table.deleteTable(); - table.createTable(); + dynamoDbClient.listTables() + .tableNames() + .forEach(tableName -> + dynamoDbClient.deleteTable(DeleteTableRequest.builder().tableName(tableName).build()) + ); } @Test void shouldReturnName() { - Assertions.assertThat(documentManager.name()).isEqualTo(database); + try (var manager = getDocumentManagerCannotCreateTables()) { + var database = CONFIG + .getSettings() + .get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); + Assertions.assertThat(manager.name()).isEqualTo(database); + } } @Test void shouldReturnErrorWhenInsertNull() { - assertSoftly(softly -> { - softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null)) - .as("should return error when insert a null DocumentEntity reference") - .isExactlyInstanceOf(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, Duration.ofSeconds(1))) - .as("should return error when insert a null DocumentEntity reference with TTL param") - .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, null)) - .as("should return error when insert a null DocumentEntity reference with nullable TTL param") - .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert(DocumentEntityGenerator.createRandomEntity(), null)) - .as("should return error when insert a null DocumentEntity reference with nullable TTL param") - .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null)) - .as("should return error when insert a null Iterable reference") - .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null, Duration.ofSeconds(1))) - .as("should return error when insert a null Iterable reference with TTL param") - .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert(List.of(DocumentEntityGenerator.createRandomEntity()), null)) - .as("should return error when insert a null Iterable reference with nullable TTL param") - .isInstanceOfAny(NullPointerException.class); - }); + + try (var documentManager = getDocumentManagerCannotCreateTables()) { + assertSoftly(softly -> { + softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null)) + .as("should return error when insert a null DocumentEntity reference") + .isExactlyInstanceOf(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, Duration.ofSeconds(1))) + .as("should return error when insert a null DocumentEntity reference with TTL param") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, null)) + .as("should return error when insert a null DocumentEntity reference with nullable TTL param") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert(DocumentEntityGenerator.createRandomEntity(), null)) + .as("should return error when insert a null DocumentEntity reference with nullable TTL param") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null)) + .as("should return error when insert a null Iterable reference") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null, Duration.ofSeconds(1))) + .as("should return error when insert a null Iterable reference with TTL param") + .isInstanceOfAny(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.insert(List.of(DocumentEntityGenerator.createRandomEntity()), null)) + .as("should return error when insert a null Iterable reference with nullable TTL param") + .isInstanceOfAny(NullPointerException.class); + }); + } } @Test void shouldInsert() { - assertSoftly(softly -> { - DocumentEntity entity = createRandomEntity(); - var _entityType = entity.name(); - var id = entity.find("_id", String.class).orElseThrow(); - var persistedEntity = documentManager.insert(entity); - softly.assertThat(persistedEntity) - .as("documentManager.insert(DocumentEntity) method should return a non-null persistent DocumentEntity") - .isNotNull(); + try (var documentManager = getDocumentManagerCanCreateTables()) { - EnhancedDocument persistedItem = table.getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); + assertSoftly(softly -> { + DocumentEntity entity = createRandomEntity(); + var _entityType = entity.name(); + var id = entity.find("_id", String.class).orElseThrow(); + var persistedEntity = documentManager.insert(entity); + softly.assertThat(persistedEntity) + .as("documentManager.insert(DocumentEntity) method should return a non-null persistent DocumentEntity") + .isNotNull(); - softly.assertThat(persistedItem).as("should return the item from dynamodb").isNotNull(); - }); + EnhancedDocument persistedItem = getTable(entity.name()).getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); - assertSoftly(softly -> { - var entities = List.of(createRandomEntity(),createRandomEntity(),createRandomEntity()); - Iterable persistedEntities = documentManager.insert(entities); - softly.assertThat(persistedEntities) - .as("documentManager.insert(Iterable<>) should returns the non-null list of DocumentEntity").isNotNull(); + softly.assertThat(persistedItem).as("should return the item from dynamodb").isNotNull(); + }); - assertThat(persistedEntities) - .as("documentmanager.insert(iterable<>) should returns a corresponded list of DocumentEntity") - .hasSize(3); + assertSoftly(softly -> { + var entities = List.of(createRandomEntity(), createRandomEntity(), createRandomEntity()); + Iterable persistedEntities = documentManager.insert(entities); + softly.assertThat(persistedEntities) + .as("documentManager.insert(Iterable<>) should returns the non-null list of DocumentEntity").isNotNull(); - persistedEntities.forEach(entity -> { - var _entityType = entity.name(); - var id = entity.find("_id", String.class).orElseThrow(); - EnhancedDocument persistedItem = table.getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); - softly.assertThat(persistedItem) - .as("all items of the list of DocumentEntity should be stored on dynamodb database. the entity %s not found" - .formatted(id)) - .isNotNull(); + assertThat(persistedEntities) + .as("documentmanager.insert(iterable<>) should returns a corresponded list of DocumentEntity") + .hasSize(3); + + var table = getTable(entities.get(0).name()); + + + persistedEntities.forEach(entity -> { + var _entityType = entity.name(); + var id = entity.find("_id", String.class).orElseThrow(); + EnhancedDocument persistedItem = table.getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); + softly.assertThat(persistedItem) + .as("all items of the list of DocumentEntity should be stored on dynamodb database. the entity %s not found" + .formatted(id)) + .isNotNull(); + }); }); - }); + } + } + private DynamoDbTable getTable(String name) { + return dynamoDbEnhancedClient + .table(name, TableSchema.documentSchemaBuilder() + .addIndexPartitionKey(TableMetadata.primaryIndexName(), entityNameResolver.apply(name), AttributeValueType.S) + .addIndexSortKey(TableMetadata.primaryIndexName(), DocumentEntityConverter.ID, AttributeValueType.S) + .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) + .build()); } @Test - void shouldInserts() { + void shouldCountByCollectionName() { - DocumentEntity entity = createRandomEntity(); - var _entityType = entity.name(); - var id = entity.find("_id", String.class).orElseThrow(); - var persistedEntity = documentManager.insert(entity); - assertSoftly(softly -> { - softly.assertThat(persistedEntity).as("should return the persistent document entity from documentManager.insert() method").isNotNull(); - EnhancedDocument persistedItem = table.getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); - softly.assertThat(persistedItem).as("should return the item from dynamodb").isNotNull(); - }); + try (var dmCanCreateTable = getDocumentManagerCanCreateTables(); + var dmCannotCreateTable = getDocumentManagerCannotCreateTables()) { + assertSoftly(softly -> { + DocumentEntity entity = createRandomEntity(); + DocumentEntity entity2 = createRandomEntity(); - } + dmCanCreateTable.insert(entity); + dmCanCreateTable.insert(entity2); + + softly.assertThatThrownBy(() -> dmCanCreateTable.count((String) null)) + .as("should return an error when a nullable String is passed as arg") + .isInstanceOfAny(NullPointerException.class); + + softly.assertThat(dmCanCreateTable.count(entity.name())) + .as("the returned count number of items from an given existent table name is incorrect") + .isEqualTo(2L); + + String nonExistentTable = UUID.randomUUID().toString(); + + softly.assertThat(dmCannotCreateTable.count(nonExistentTable)) + .as("the returned count number of items from a given an non-existent table name is incorrect") + .isEqualTo(0L); + softly.assertThatThrownBy(() -> getTable(nonExistentTable).describeTable()) + .as("it must not create a table") + .isInstanceOfAny(ResourceNotFoundException.class); + var entityBName = "entityB"; + DocumentEntity entity3 = createRandomEntity(entityBName); + dmCanCreateTable.insert(entity3); + + softly.assertThat(dmCannotCreateTable.count(entity3.name())) + .as("the returned count number of items from a given table name is incorrect") + .isEqualTo(1L); + }); + + } + } } From e7ecf0ff6be7680f31b26198b366301246bdb109 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Tue, 16 Jan 2024 03:59:23 -0300 Subject: [PATCH 38/70] refactor: removed unnecessary dependency Signed-off-by: Maximillian Arruda --- jnosql-dynamodb/pom.xml | 5 - .../DocumentEntityConverter.java | 96 ++++++++++---- .../DynamoDBDocumentManager.java | 124 +++++++++++------- .../DocumentEntityConverterTest.java | 91 ++++--------- .../DynamoDBDocumentManagerTest.java | 58 ++++---- .../communication/DynamoDBTestUtils.java | 31 ----- 6 files changed, 196 insertions(+), 209 deletions(-) diff --git a/jnosql-dynamodb/pom.xml b/jnosql-dynamodb/pom.xml index 73a82aee2..b8ecf9c11 100644 --- a/jnosql-dynamodb/pom.xml +++ b/jnosql-dynamodb/pom.xml @@ -48,11 +48,6 @@ dynamodb ${dynamodb.version} - - software.amazon.awssdk - dynamodb-enhanced - ${dynamodb.version} - net.datafaker datafaker diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java index e5c1a18e3..88b547627 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java @@ -21,10 +21,15 @@ import org.eclipse.jnosql.communication.driver.JsonbSupplier; import org.eclipse.jnosql.communication.driver.ValueUtil; import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import java.util.*; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.UnaryOperator; import java.util.stream.StreamSupport; @@ -41,25 +46,6 @@ class DocumentEntityConverter { private DocumentEntityConverter() { } - static DocumentEntity toDocumentEntity(UnaryOperator entityNameResolver, EnhancedDocument enhancedDocument) { - if (enhancedDocument == null) { - return null; - } - if (enhancedDocument.toMap().isEmpty()) { - return null; - } - UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); - String entityAttribute = resolver.apply(ENTITY); - Map map = enhancedDocument.toMap(); - var entityName = map.containsKey(entityAttribute) ? map.get(entityAttribute).s() : entityAttribute; - List documents = map.entrySet() - .stream() - .filter(entry -> !Objects.equals(entityAttribute, entry.getKey())) - .map(entry -> Document.of(entry.getKey(), convertValue(entry.getValue()))) - .toList(); - return DocumentEntity.of(entityName, documents); - } - private static Object convertValue(Object value) { if (value instanceof AttributeValue attributeValue) { switch (attributeValue.type()) { @@ -91,14 +77,6 @@ private static Object convertValue(Object value) { return value; } - static EnhancedDocument toEnhancedDocument(UnaryOperator entityNameResolver, DocumentEntity documentEntity) { - UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); - Map documentAsMap = getMap(resolver, documentEntity); - return EnhancedDocument.builder() - .json(JSONB.toJson(documentAsMap)) - .build(); - } - static Map getMap(UnaryOperator entityNameResolver, DocumentEntity entity) { var nameResolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); Map jsonObject = new HashMap<>(); @@ -141,4 +119,64 @@ private static boolean isSudDocumentList(Object value) { return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). allMatch(d -> d instanceof Iterable && isSudDocument(d)); } + + public static Map toItem(UnaryOperator entityNameResolver, DocumentEntity documentEntity) { + UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); + Map documentAttributes = getMap(resolver, documentEntity); + return toItem(documentAttributes); + } + + private static Map toItem(Map documentAttributes) { + HashMap result = new HashMap<>(); + documentAttributes.forEach((attribute, value) -> result.put(attribute, toAttributeValue(value))); + return result; + } + + public static AttributeValue toAttributeValue(Object value) { + if (value == null) + return AttributeValue.builder().nul(true).build(); + if (value instanceof String str) + return AttributeValue.builder().s(str).build(); + if (value instanceof Number number) + return AttributeValue.builder().n(String.valueOf(number)).build(); + if (value instanceof Boolean bool) + return AttributeValue.builder().bool(bool).build(); + if (value instanceof List list) + return AttributeValue.builder().l(list.stream().filter(Objects::nonNull) + .map(DocumentEntityConverter::toAttributeValue).toList()).build(); + if (value instanceof Map mapValue) { + HashMap values = new HashMap<>(); + mapValue.forEach((k, v) -> values.put(String.valueOf(k), toAttributeValue(v))); + return AttributeValue.builder().m(values).build(); + } + if (value instanceof byte[] data) { + return AttributeValue.builder().b(SdkBytes.fromByteArray(data)).build(); + } + if (value instanceof ByteBuffer byteBuffer) { + return AttributeValue.builder().b(SdkBytes.fromByteBuffer(byteBuffer)).build(); + } + if (value instanceof InputStream input) { + return AttributeValue.builder().b(SdkBytes.fromInputStream(input)).build(); + } + return AttributeValue.builder().s(String.valueOf(value)).build(); + } + + + public static DocumentEntity toDocumentEntity(UnaryOperator entityNameResolver, Map item) { + if (item == null) { + return null; + } + if (item.isEmpty()) { + return null; + } + UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); + String entityAttribute = resolver.apply(ENTITY); + var entityName = item.containsKey(entityAttribute) ? item.get(entityAttribute).s() : entityAttribute; + List documents = item.entrySet() + .stream() + .filter(entry -> !Objects.equals(entityAttribute, entry.getKey())) + .map(entry -> Document.of(entry.getKey(), convertValue(entry.getValue()))) + .toList(); + return DocumentEntity.of(entityName, documents); + } } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java index 785ade728..36560d268 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -20,22 +20,29 @@ import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.communication.document.DocumentQuery; -import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider; -import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; -import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; -import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; -import software.amazon.awssdk.enhanced.dynamodb.TableMetadata; -import software.amazon.awssdk.enhanced.dynamodb.TableSchema; -import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse; import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveRequest; import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveResponse; +import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; +import software.amazon.awssdk.services.dynamodb.model.KeyType; +import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; +import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; +import software.amazon.awssdk.services.dynamodb.model.StreamSpecification; import software.amazon.awssdk.services.dynamodb.model.TimeToLiveStatus; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; @@ -44,7 +51,7 @@ import java.util.stream.StreamSupport; import static java.util.Objects.requireNonNull; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toEnhancedDocument; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItem; public class DynamoDBDocumentManager implements DocumentManager { @@ -54,18 +61,14 @@ public class DynamoDBDocumentManager implements DocumentManager { private final DynamoDbClient dynamoDbClient; - private final DynamoDbEnhancedClient dynamoDbEnhancedClient; - private final UnaryOperator entityNameAttributeNameResolver; - private final ConcurrentHashMap> tables = new ConcurrentHashMap<>(); private final ConcurrentHashMap> ttlAttributeNamesByTable = new ConcurrentHashMap<>(); public DynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient, Settings settings) { this.settings = settings; this.database = database; this.dynamoDbClient = dynamoDbClient; - this.dynamoDbEnhancedClient = DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build(); this.entityNameAttributeNameResolver = this::resolveEntityNameAttributeName; } @@ -77,10 +80,6 @@ public DynamoDbClient getDynamoDbClient() { return dynamoDbClient; } - public DynamoDbEnhancedClient getDynamoDbEnhancedClient() { - return dynamoDbEnhancedClient; - } - @Override public String name() { return database; @@ -93,18 +92,15 @@ UnaryOperator entityNameAttributeNameResolver() { @Override public DocumentEntity insert(DocumentEntity documentEntity) { requireNonNull(documentEntity, "documentEntity is required"); - var enhancedDocument = convertToEnhancedDocument(documentEntity); - createIfNeeded(getTableFor(documentEntity.name())) - .putItem(enhancedDocument); + getDynamoDbClient().putItem(PutItemRequest.builder() + .tableName(createIfNeeded(documentEntity.name()).table().tableName()) + .item(asItem(documentEntity)) + .build()); return documentEntity; } - private EnhancedDocument convertToEnhancedDocument(DocumentEntity documentEntity) { - return toEnhancedDocument(entityNameAttributeNameResolver(), documentEntity); - } - - private DynamoDbTable getTableFor(String name) { - return this.tables.computeIfAbsent(name, this::buildTable); + private Map asItem(DocumentEntity documentEntity) { + return toItem(entityNameAttributeNameResolver(), documentEntity); } private Supplier getTTLAttributeNameFor(String tableName) { @@ -112,38 +108,71 @@ private Supplier getTTLAttributeNameFor(String tableName) { } private Supplier getTTLAttributeNameSupplierFromTable(String tableName) { - DynamoDbTable table = buildTable(tableName); + createIfNeeded(tableName); DescribeTimeToLiveResponse describeTimeToLiveResponse = getDynamoDbClient().describeTimeToLive(DescribeTimeToLiveRequest.builder() - .tableName(table.tableName()).build()); + .tableName(tableName).build()); if (TimeToLiveStatus.ENABLED.equals(describeTimeToLiveResponse.timeToLiveDescription().timeToLiveStatus())) { var ttlAttributeName = describeTimeToLiveResponse.timeToLiveDescription().attributeName(); return () -> ttlAttributeName; } - return unsupportedTTLSupplierFor(table.tableName()); + return unsupportedTTLSupplierFor(tableName); } private Supplier unsupportedTTLSupplierFor(String tableName) { return () -> tableName + " don't support TTL operations. Check if TTL support is enabled for this table."; } - private DynamoDbTable buildTable(String nameKey) { - return dynamoDbEnhancedClient - .table(nameKey, TableSchema.documentSchemaBuilder() - .addIndexPartitionKey(TableMetadata.primaryIndexName(), getEntityNameAttributeName(), AttributeValueType.S) - .addIndexSortKey(TableMetadata.primaryIndexName(), DocumentEntityConverter.ID, AttributeValueType.S) - .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) - .build()); - } - - private DynamoDbTable createIfNeeded(DynamoDbTable table) { + private DescribeTableResponse createIfNeeded(String tableName) { if (shouldCreateTables()) { try { - table.describeTable(); + return getDynamoDbClient().describeTable(DescribeTableRequest.builder() + .tableName(tableName) + .build()); } catch (ResourceNotFoundException ex) { - table.createTable(); + return createTable(tableName); } } - return table; + return getDynamoDbClient().describeTable(DescribeTableRequest.builder() + .tableName(tableName) + .build()); + } + + private DescribeTableResponse createTable(String tableName) { + try (var waiter = getDynamoDbClient().waiter()) { + getDynamoDbClient().createTable(CreateTableRequest.builder() + .tableName(tableName) + .keySchema(defaultKeySchemaFor(tableName)) + .attributeDefinitions(defaultAttributeDefinitionsFor(tableName)) + .provisionedThroughput(defaultProvisionedThroughputFor(tableName)) + .streamSpecification(defaultStreamSpecificationFor(tableName)) + .build()); + + var tableRequest = DescribeTableRequest.builder().tableName(tableName).build(); + var waiterResponse = waiter.waitUntilTableExists(tableRequest); + return waiterResponse.matched().response().orElseThrow(); + } + } + + private StreamSpecification defaultStreamSpecificationFor(String tableName) { + return null; + } + + private ProvisionedThroughput defaultProvisionedThroughputFor(String tableName) { + return DynamoTableUtils.createProvisionedThroughput(null, null); + } + + private Collection defaultAttributeDefinitionsFor(String tableName) { + return List.of( + AttributeDefinition.builder().attributeName(getEntityNameAttributeName()).attributeType(ScalarAttributeType.S).build(), + AttributeDefinition.builder().attributeName(DocumentEntityConverter.ID).attributeType(ScalarAttributeType.S).build() + ); + } + + private Collection defaultKeySchemaFor(String tableName) { + return List.of( + KeySchemaElement.builder().attributeName(getEntityNameAttributeName()).keyType(KeyType.HASH).build(), + KeySchemaElement.builder().attributeName(DocumentEntityConverter.ID).keyType(KeyType.RANGE).build() + ); } private boolean shouldCreateTables() { @@ -160,11 +189,8 @@ private String getEntityNameAttributeName() { public DocumentEntity insert(DocumentEntity documentEntity, Duration ttl) { requireNonNull(documentEntity, "documentEntity is required"); requireNonNull(ttl, "ttl is required"); - DynamoDbTable tableFor = createIfNeeded(getTableFor(documentEntity.name())); - documentEntity.add(getTTLAttributeNameFor(tableFor.tableName()).get(), Instant.now().plus(ttl).truncatedTo(ChronoUnit.SECONDS)); - var enhancedDocument = convertToEnhancedDocument(documentEntity); - tableFor.putItem(enhancedDocument); - return documentEntity; + documentEntity.add(getTTLAttributeNameFor(documentEntity.name()).get(), Instant.now().plus(ttl).truncatedTo(ChronoUnit.SECONDS)); + return insert(documentEntity); } @Override @@ -210,9 +236,11 @@ public Stream select(DocumentQuery documentQuery) { @Override public long count(String tableName) { Objects.requireNonNull(tableName, "tableName is required"); - DynamoDbTable table = getTableFor(tableName); try { - return table.describeTable().table().itemCount(); + return getDynamoDbClient() + .describeTable(DescribeTableRequest.builder().tableName(tableName).build()) + .table() + .itemCount(); } catch (ResourceNotFoundException ex) { return 0; } diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java index 1f53a6672..fd6aaa3be 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java @@ -17,15 +17,16 @@ import jakarta.json.Json; import jakarta.json.bind.Jsonb; -import org.eclipse.jnosql.communication.TypeReference; +import net.datafaker.Faker; import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.driver.JsonbSupplier; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; -import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import java.io.StringReader; import java.util.List; +import java.util.UUID; import java.util.function.UnaryOperator; import static org.assertj.core.api.SoftAssertions.assertSoftly; @@ -35,84 +36,40 @@ @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) class DocumentEntityConverterTest { + static final Faker faker = new Faker(); + private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); - private static final UnaryOperator entityNameResolver = UnaryOperator.identity(); + private static final UnaryOperator entityNameResolver = UnaryOperator.identity(); @Test - void shouldConvertDocumentEntityToEnhancedDocument() { + void shouldConvertToItemRequest() { assertSoftly(softly -> { - var entity = DocumentEntityGenerator.createRandomEntity(); - var enhancedDocument = DocumentEntityConverter.toEnhancedDocument(entityNameResolver, entity); - var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); - var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); - softly.assertThat(actual).as("cannot convert a simple DocumentEntity") - .isEqualTo(expected); - }); - } + var entity = DocumentEntity.of("entityA", + List.of( + Document.of("_id", UUID.randomUUID().toString()), + Document.of("city", faker.address().city()), + Document.of("total", 10.0), + Document.of("address", List.of( + Document.of("zipcode", faker.address().zipCode()), + Document.of("city", faker.address().cityName()))), + Document.of("phones", List.of(faker.name().firstName(), faker.name().firstName(), faker.name().firstName())) + )); - @Test - void shouldConvertDocumentEntityWithSubDocumentsToEnhancedDocument() { + var item = DocumentEntityConverter.toItem(entityNameResolver, entity); + + var entityFromItem = DocumentEntityConverter.toDocumentEntity(entityNameResolver, item); - assertSoftly(softly -> { - var entity = DocumentEntityGenerator.createRandomEntityWithSubDocuments(3); - var enhancedDocument = DocumentEntityConverter.toEnhancedDocument(entityNameResolver, entity); var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); - var actual = Json.createReader(new StringReader(enhancedDocument.toJson())).readObject(); - softly.assertThat(actual).as("cannot convert a DocumentEntity with document sublist") - .isEqualTo(expected); - }); - } + var actual = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entityFromItem)))).readObject(); - @Test - void shouldConvertEnhancedDocumentToDocumentEntity() { - - var enhancedDocument = EnhancedDocument.builder() - .json(""" - { - "%s":"Max", - "%s": "person", - "name":"Maximillian", - "number": 123, - "address": { - "street": "Rua tralala" - }, - "phones": [ - "1111-2222", - "2222-3333" - ] - } - """.formatted(DocumentEntityConverter.ID, DocumentEntityConverter.ENTITY)).build(); - var expected = DocumentEntityConverter.toDocumentEntity(entityNameResolver, enhancedDocument); + softly.assertThat(actual).as("cannot convert a simple DocumentEntity") + .isEqualTo(expected); + }); - assertSoftly(softly -> { - softly.assertThat(expected).as("cannot return a null reference") - .isNotNull(); - softly.assertThat(expected.name()).as("documentEntity name is not correct") - .isEqualTo("person"); - - softly.assertThat(expected.find("_id", String.class)) - .as("documentEntity._id was parsed incorrectly") - .hasValue("Max"); - softly.assertThat(expected.find("name", String.class)) - .as("documentEntity.name was parsed incorrectly") - .hasValue("Maximillian"); - softly.assertThat(expected.find("number", Integer.class)) - .as("documentEntity.number was parsed incorrectly") - .hasValue(123); - softly.assertThat(expected.find("address", new TypeReference>() { - })) - .as("documentEntity.address was parsed incorrectly") - .contains(List.of(Document.of("street", "Rua tralala"))); - softly.assertThat(expected.find("phones", new TypeReference>() { - })) - .as("documentEntity.phones was parsed incorrectly") - .contains(List.of("1111-2222", "2222-3333")); - }); } - } diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java index dfb2216d0..dbbd8503e 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java @@ -18,7 +18,6 @@ import org.assertj.core.api.Assertions; import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.communication.SettingsBuilder; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; @@ -26,23 +25,17 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; -import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider; -import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; -import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; -import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; -import software.amazon.awssdk.enhanced.dynamodb.Key; -import software.amazon.awssdk.enhanced.dynamodb.TableMetadata; -import software.amazon.awssdk.enhanced.dynamodb.TableSchema; -import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; +import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; import java.time.Duration; import java.util.List; +import java.util.Map; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.function.UnaryOperator; import static org.assertj.core.api.Assertions.assertThat; @@ -57,9 +50,6 @@ class DynamoDBDocumentManagerTest { private DynamoDbClient dynamoDbClient; - - private DynamoDbEnhancedClient dynamoDbEnhancedClient; - private UnaryOperator entityNameResolver; @@ -68,7 +58,7 @@ void setUp() { var settings = CONFIG.getSettings(); entityNameResolver = entityName -> settings.get(DynamoDBConfigurations.ENTITY_PARTITION_KEY, String.class).orElse(entityName); dynamoDbClient = CONFIG.getDynamoDbClient(settings); - dynamoDbEnhancedClient = CONFIG.getDynamoDbEnhancedClient(dynamoDbClient); + tearDown(); } private DocumentManager getDocumentManagerCannotCreateTables() { @@ -145,13 +135,14 @@ void shouldInsert() { assertSoftly(softly -> { DocumentEntity entity = createRandomEntity(); var _entityType = entity.name(); - var id = entity.find("_id", String.class).orElseThrow(); + var id = entity.find(DocumentEntityConverter.ID, String.class).orElseThrow(); var persistedEntity = documentManager.insert(entity); + softly.assertThat(persistedEntity) .as("documentManager.insert(DocumentEntity) method should return a non-null persistent DocumentEntity") .isNotNull(); - EnhancedDocument persistedItem = getTable(entity.name()).getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); + var persistedItem = getItem(_entityType, id); softly.assertThat(persistedItem).as("should return the item from dynamodb").isNotNull(); }); @@ -166,13 +157,10 @@ void shouldInsert() { .as("documentmanager.insert(iterable<>) should returns a corresponded list of DocumentEntity") .hasSize(3); - var table = getTable(entities.get(0).name()); - - persistedEntities.forEach(entity -> { var _entityType = entity.name(); var id = entity.find("_id", String.class).orElseThrow(); - EnhancedDocument persistedItem = table.getItem(Key.builder().partitionValue(_entityType).sortValue(id).build()); + var persistedItem = getItem(_entityType, id); softly.assertThat(persistedItem) .as("all items of the list of DocumentEntity should be stored on dynamodb database. the entity %s not found" .formatted(id)) @@ -182,15 +170,19 @@ void shouldInsert() { } } - private DynamoDbTable getTable(String name) { - return dynamoDbEnhancedClient - .table(name, TableSchema.documentSchemaBuilder() - .addIndexPartitionKey(TableMetadata.primaryIndexName(), entityNameResolver.apply(name), AttributeValueType.S) - .addIndexSortKey(TableMetadata.primaryIndexName(), DocumentEntityConverter.ID, AttributeValueType.S) - .attributeConverterProviders(AttributeConverterProvider.defaultProvider()) - .build()); + private Map getItem(String _entityType, String id) { + return dynamoDbClient + .getItem(GetItemRequest.builder() + .tableName(_entityType) + .key(Map.of( + entityNameResolver.apply(_entityType), AttributeValue.builder().s(_entityType).build(), + DocumentEntityConverter.ID, AttributeValue.builder().s(id).build() + )) + .build()) + .item(); } + @Test void shouldCountByCollectionName() { @@ -218,7 +210,11 @@ void shouldCountByCollectionName() { .as("the returned count number of items from a given an non-existent table name is incorrect") .isEqualTo(0L); - softly.assertThatThrownBy(() -> getTable(nonExistentTable).describeTable()) + softly.assertThatThrownBy(() -> dynamoDbClient + .describeTable(DescribeTableRequest + .builder() + .tableName(nonExistentTable + ).build())) .as("it must not create a table") .isInstanceOfAny(ResourceNotFoundException.class); @@ -233,4 +229,8 @@ void shouldCountByCollectionName() { } } + + @Test + void shouldCountByDocumentQuery() { + } } diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index f29d4929c..dd987923b 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -22,16 +22,8 @@ import org.jetbrains.annotations.NotNull; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; -import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; -import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; -import software.amazon.awssdk.enhanced.dynamodb.TableSchema; -import software.amazon.awssdk.enhanced.dynamodb.document.DocumentTableSchema; -import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument; -import software.amazon.awssdk.enhanced.dynamodb.model.DescribeTableEnhancedResponse; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import java.util.function.Function; import java.util.function.UnaryOperator; enum DynamoDBTestUtils { @@ -127,27 +119,4 @@ DynamoDbClient getDynamoDbClient(Settings settings) { return builderSync.build(); } - DynamoDbEnhancedClient getDynamoDbEnhancedClient() { - DynamoDbClient dynamoDbClient = getDynamoDbClient(); - return getDynamoDbEnhancedClient(dynamoDbClient); - } - - DynamoDbEnhancedClient getDynamoDbEnhancedClient(DynamoDbClient dynamoDbClient) { - return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build(); - } - - public static DescribeTableEnhancedResponse createTable(DynamoDbEnhancedClient enhancedClient, String tableName, Function documentTableSchema) { - DynamoDbTable table = enhancedClient.table(tableName, - documentTableSchema.apply(TableSchema.documentSchemaBuilder()) - .build() - ); - table.createTable(); - return table.describeTable(); - } - - public static void deleteTable(DynamoDbClient dynamoDbClient, Function deleteTableRequest) { - var request = deleteTableRequest.apply(DeleteTableRequest.builder()); - dynamoDbClient.deleteTable(request); - } - } From ff238fb40a5578eb4e6c828ec1b975e071dbb72a Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sat, 20 Jan 2024 23:04:10 -0300 Subject: [PATCH 39/70] chore: add conversion for update operations Signed-off-by: Maximillian Arruda --- .../DocumentEntityConverter.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java index 88b547627..2a31a2c1c 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java @@ -21,7 +21,9 @@ import org.eclipse.jnosql.communication.driver.JsonbSupplier; import org.eclipse.jnosql.communication.driver.ValueUtil; import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.services.dynamodb.model.AttributeAction; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate; import java.io.InputStream; import java.nio.ByteBuffer; @@ -81,10 +83,14 @@ static Map getMap(UnaryOperator entityNameResolver, Docu var nameResolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); Map jsonObject = new HashMap<>(); entity.documents().forEach(feedJSON(jsonObject)); - jsonObject.put(Optional.ofNullable(nameResolver.apply(ENTITY)).orElse(ENTITY), entity.name()); + jsonObject.put(entityAttributeName(nameResolver), entity.name()); return jsonObject; } + public static String entityAttributeName(UnaryOperator nameResolver) { + return Optional.ofNullable(nameResolver.apply(ENTITY)).orElse(ENTITY); + } + private static Consumer feedJSON(Map jsonObject) { return d -> { Object value = ValueUtil.convert(d.value()); @@ -126,6 +132,7 @@ public static Map toItem(UnaryOperator entityNam return toItem(documentAttributes); } + private static Map toItem(Map documentAttributes) { HashMap result = new HashMap<>(); documentAttributes.forEach((attribute, value) -> result.put(attribute, toAttributeValue(value))); @@ -158,9 +165,33 @@ public static AttributeValue toAttributeValue(Object value) { if (value instanceof InputStream input) { return AttributeValue.builder().b(SdkBytes.fromInputStream(input)).build(); } + if (value instanceof Document document) { + return toAttributeValue(getMap(document)); + } return AttributeValue.builder().s(String.valueOf(value)).build(); } + public static Map toItemUpdate(UnaryOperator entityNameResolver, DocumentEntity documentEntity) { + UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); + Map documentAttributes = getMap(resolver, documentEntity); + return toItemUpdate(documentAttributes); + } + + private static Map toItemUpdate(Map documentAttributes) { + return documentAttributes + .entrySet() + .stream() + .map(entry -> Map.of(entry.getKey(), toAttributeValueUpdate(entry.getValue()))) + .reduce(new HashMap<>(), (a, b) -> { + a.putAll(b); + return a; + }); + } + + public static AttributeValueUpdate toAttributeValueUpdate(Object value) { + return AttributeValueUpdate.builder().value(toAttributeValue(value)).action(AttributeAction.PUT).build(); + } + public static DocumentEntity toDocumentEntity(UnaryOperator entityNameResolver, Map item) { if (item == null) { From 6f047131eb5083892657790b484eab9f8110a897 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sat, 20 Jan 2024 23:08:31 -0300 Subject: [PATCH 40/70] test: add tests for update operation Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManagerTest.java | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java index dbbd8503e..94821eeb3 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java @@ -16,8 +16,11 @@ package org.eclipse.jnosql.databases.dynamodb.communication; +import net.datafaker.Faker; import org.assertj.core.api.Assertions; +import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.document.Document; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; @@ -36,6 +39,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.function.BiConsumer; import java.util.function.UnaryOperator; import static org.assertj.core.api.Assertions.assertThat; @@ -48,10 +52,11 @@ @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) class DynamoDBDocumentManagerTest { + private static Faker faker = new Faker(); private DynamoDbClient dynamoDbClient; - private UnaryOperator entityNameResolver; + private UnaryOperator entityNameResolver; @BeforeEach void setUp() { @@ -170,6 +175,52 @@ void shouldInsert() { } } + @Test + void shouldUpdate() { + try (var documentManager = getDocumentManagerCanCreateTables()) { + + var entity1 = createRandomEntity(); + var entity2 = createRandomEntity(); + var entity3 = createRandomEntity(); + + documentManager.insert(List.of(entity1, entity2, entity3)); + + final BiConsumer assertions = (softly, updatedEntity) -> { + Map item = getItem(updatedEntity.name(), updatedEntity.find(DocumentEntityConverter.ID, String.class).orElseThrow()); + softly.assertThat(item.get("name")) + .as("the name attribute should exists in the returned item from dynamodb") + .isNotNull(); + softly.assertThat(item.get("name").s()) + .as("the name attribute should had be updated successfully") + .isEqualTo(updatedEntity.find("name", String.class).orElse(null)); + }; + + assertSoftly(softly -> { + entity1.add(Document.of("name", faker.name().fullName())); + var updatedEntity = documentManager.update(entity1); + softly.assertThat(updatedEntity) + .as("documentManager.update(DocumentEntity) method should return a non-null persistent DocumentEntity") + .isNotNull(); + assertions.accept(softly, updatedEntity); + }); + + assertSoftly(softly -> { + entity2.add(Document.of("name", faker.name().fullName())); + entity3.add(Document.of("name", faker.name().fullName())); + + var updatedEntities = documentManager.update(List.of(entity2, entity2)); + softly.assertThat(updatedEntities) + .as("documentManager.update(Iterable<>) method should return a non-null list of DocumentEntity") + .isNotNull(); + softly.assertThat(updatedEntities) + .as("the size of the returned list of DocumentEntity from " + + "documentManager.update(Iterable<>) method should be equals to the size of the submitted list of DocumentEntity") + .hasSize(2); + updatedEntities.forEach(updatedEntity -> assertions.accept(softly, updatedEntity)); + }); + } + } + private Map getItem(String _entityType, String id) { return dynamoDbClient .getItem(GetItemRequest.builder() From 3ac2b24f9a151ccf83714954d767b023afbce7bc Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sat, 20 Jan 2024 23:09:20 -0300 Subject: [PATCH 41/70] feat: first implementation for the update operation Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManager.java | 87 ++++++++++++------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java index 36560d268..6c9313324 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -23,6 +23,7 @@ import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate; import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse; @@ -36,22 +37,26 @@ import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; import software.amazon.awssdk.services.dynamodb.model.StreamSpecification; import software.amazon.awssdk.services.dynamodb.model.TimeToLiveStatus; +import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; -import java.util.function.UnaryOperator; import java.util.stream.Stream; import java.util.stream.StreamSupport; import static java.util.Objects.requireNonNull; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.entityAttributeName; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toAttributeValue; import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItem; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItemUpdate; public class DynamoDBDocumentManager implements DocumentManager { @@ -61,15 +66,14 @@ public class DynamoDBDocumentManager implements DocumentManager { private final DynamoDbClient dynamoDbClient; - private final UnaryOperator entityNameAttributeNameResolver; - private final ConcurrentHashMap> ttlAttributeNamesByTable = new ConcurrentHashMap<>(); + private final ConcurrentHashMap tables = new ConcurrentHashMap<>(); + public DynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient, Settings settings) { this.settings = settings; this.database = database; this.dynamoDbClient = dynamoDbClient; - this.entityNameAttributeNameResolver = this::resolveEntityNameAttributeName; } private String resolveEntityNameAttributeName(String entityName) { @@ -85,24 +89,16 @@ public String name() { return database; } - UnaryOperator entityNameAttributeNameResolver() { - return this.entityNameAttributeNameResolver; - } - @Override public DocumentEntity insert(DocumentEntity documentEntity) { requireNonNull(documentEntity, "documentEntity is required"); getDynamoDbClient().putItem(PutItemRequest.builder() .tableName(createIfNeeded(documentEntity.name()).table().tableName()) - .item(asItem(documentEntity)) + .item(toItem(this::resolveEntityNameAttributeName, documentEntity)) .build()); return documentEntity; } - private Map asItem(DocumentEntity documentEntity) { - return toItem(entityNameAttributeNameResolver(), documentEntity); - } - private Supplier getTTLAttributeNameFor(String tableName) { return this.ttlAttributeNamesByTable.computeIfAbsent(tableName, this::getTTLAttributeNameSupplierFromTable); } @@ -123,15 +119,20 @@ private Supplier unsupportedTTLSupplierFor(String tableName) { } private DescribeTableResponse createIfNeeded(String tableName) { - if (shouldCreateTables()) { - try { - return getDynamoDbClient().describeTable(DescribeTableRequest.builder() - .tableName(tableName) - .build()); - } catch (ResourceNotFoundException ex) { - return createTable(tableName); - } + return this.tables.computeIfAbsent(tableName, this::resolveTable); + } + + private DescribeTableResponse resolveTable(String tableName) { + try { + return getDescribeTableResponse(tableName); + } catch (ResourceNotFoundException ex) { + if (!shouldCreateTables()) + throw ex; + return createTable(tableName); } + } + + private DescribeTableResponse getDescribeTableResponse(String tableName) { return getDynamoDbClient().describeTable(DescribeTableRequest.builder() .tableName(tableName) .build()); @@ -163,14 +164,14 @@ private ProvisionedThroughput defaultProvisionedThroughputFor(String tableName) private Collection defaultAttributeDefinitionsFor(String tableName) { return List.of( - AttributeDefinition.builder().attributeName(getEntityNameAttributeName()).attributeType(ScalarAttributeType.S).build(), + AttributeDefinition.builder().attributeName(getEntityAttributeName()).attributeType(ScalarAttributeType.S).build(), AttributeDefinition.builder().attributeName(DocumentEntityConverter.ID).attributeType(ScalarAttributeType.S).build() ); } private Collection defaultKeySchemaFor(String tableName) { return List.of( - KeySchemaElement.builder().attributeName(getEntityNameAttributeName()).keyType(KeyType.HASH).build(), + KeySchemaElement.builder().attributeName(getEntityAttributeName()).keyType(KeyType.HASH).build(), KeySchemaElement.builder().attributeName(DocumentEntityConverter.ID).keyType(KeyType.RANGE).build() ); } @@ -181,8 +182,8 @@ private boolean shouldCreateTables() { .orElse(false); } - private String getEntityNameAttributeName() { - return entityNameAttributeNameResolver().apply(DocumentEntityConverter.ENTITY); + private String getEntityAttributeName() { + return entityAttributeName(this::resolveEntityNameAttributeName); } @Override @@ -195,7 +196,7 @@ public DocumentEntity insert(DocumentEntity documentEntity, Duration ttl) { @Override public Iterable insert(Iterable entities) { - requireNonNull(entities, "entities is required"); + requireNonNull(entities, "entities are required"); return StreamSupport.stream(entities.spliterator(), false) .map(this::insert) .toList(); @@ -212,7 +213,36 @@ public Iterable insert(Iterable entities, Durati @Override public DocumentEntity update(DocumentEntity documentEntity) { - throw new UnsupportedOperationException("update method must be implemented!"); + requireNonNull(documentEntity, "entity is required"); + Map itemKey = getItemKey(documentEntity); + Map attributeUpdates = asItemToUpdate(documentEntity); + itemKey.keySet().forEach(attributeUpdates::remove); + getDynamoDbClient().updateItem(UpdateItemRequest.builder() + .tableName(createIfNeeded(documentEntity.name()).table().tableName()) + .key(itemKey) + .attributeUpdates(attributeUpdates) + .build()); + return documentEntity; + } + + private Map getItemKey(DocumentEntity documentEntity) { + DescribeTableResponse describeTableResponse = this.tables.computeIfAbsent(documentEntity.name(), this::getDescribeTableResponse); + Map itemKey = describeTableResponse + .table() + .keySchema() + .stream() + .map(attribute -> Map.of(attribute.attributeName(), + toAttributeValue(documentEntity.find(attribute.attributeName(), Object.class).orElse(null)))) + .reduce(new HashMap<>(), (a, b) -> { + a.putAll(b); + return a; + }); + itemKey.put(getEntityAttributeName(), toAttributeValue(documentEntity.name())); + return itemKey; + } + + private Map asItemToUpdate(DocumentEntity documentEntity) { + return toItemUpdate(this::resolveEntityNameAttributeName, documentEntity); } @Override @@ -237,8 +267,7 @@ public Stream select(DocumentQuery documentQuery) { public long count(String tableName) { Objects.requireNonNull(tableName, "tableName is required"); try { - return getDynamoDbClient() - .describeTable(DescribeTableRequest.builder().tableName(tableName).build()) + return getDescribeTableResponse(tableName) .table() .itemCount(); } catch (ResourceNotFoundException ex) { From 29bef682570bbe27eea1d5c6058ba05a632b0b55 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sat, 20 Jan 2024 23:12:19 -0300 Subject: [PATCH 42/70] test: add more tests for the update operation Signed-off-by: Maximillian Arruda --- .../DynamoDBDocumentManagerTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java index 94821eeb3..b9383390a 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java @@ -103,7 +103,7 @@ void shouldReturnName() { } @Test - void shouldReturnErrorWhenInsertNull() { + void shouldReturnErrorWhenInsertWithInvalidInputs() { try (var documentManager = getDocumentManagerCannotCreateTables()) { assertSoftly(softly -> { @@ -175,6 +175,21 @@ void shouldInsert() { } } + @Test + void shouldReturnErrorWhenUpdateWithInvalidInputs() { + + try (var documentManager = getDocumentManagerCannotCreateTables()) { + assertSoftly(softly -> { + softly.assertThatThrownBy(() -> documentManager.update((DocumentEntity) null)) + .as("should return error when insert a null DocumentEntity reference") + .isExactlyInstanceOf(NullPointerException.class); + softly.assertThatThrownBy(() -> documentManager.update((Iterable) null)) + .as("should return error when insert a null Iterable reference") + .isInstanceOfAny(NullPointerException.class); + }); + } + } + @Test void shouldUpdate() { try (var documentManager = getDocumentManagerCanCreateTables()) { From 36e6f0fcd0d05c094b649affc95d53167ef7fcae Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 28 Jan 2024 07:28:53 -0300 Subject: [PATCH 43/70] feat: implemented delete,select,count Signed-off-by: Maximillian Arruda --- .../DefaultDynamoDBDocumentManager.java | 347 ++++++++++++++++++ .../DocumentEntityConverter.java | 2 +- .../DynamoDBDocumentManager.java | 290 ++------------- .../DynamoDBDocumentManagerFactory.java | 2 +- .../dynamodb/communication/DynamoDBQuery.java | 35 ++ .../communication/DynamoDBQueryBuilder.java | 198 ++++++++++ .../DynamoDBQuerySelectBuilder.java | 78 ++++ .../dynamodb/communication/PartiQLQuery.java | 34 ++ .../communication/PartiQLQueryBuilder.java | 129 +++++++ .../PartiQLQueryDeleteBuilder.java | 55 +++ .../PartiQLQuerySelectBuilder.java | 73 ++++ ...ltDynamoDBDocumentManagerFactoryTest.java} | 2 +- ...> DefaultDynamoDBDocumentManagerTest.java} | 155 +++++++- .../DocumentEntityGenerator.java | 6 +- 14 files changed, 1140 insertions(+), 266 deletions(-) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuery.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuerySelectBuilder.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuery.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryBuilder.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryDeleteBuilder.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuerySelectBuilder.java rename jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/{DynamoDBDocumentManagerFactoryTest.java => DefaultDynamoDBDocumentManagerFactoryTest.java} (97%) rename jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/{DynamoDBDocumentManagerTest.java => DefaultDynamoDBDocumentManagerTest.java} (70%) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java new file mode 100644 index 000000000..ba9626935 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; +import org.eclipse.jnosql.communication.document.DocumentEntity; +import org.eclipse.jnosql.communication.document.DocumentQuery; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate; +import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse; +import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveRequest; +import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveResponse; +import software.amazon.awssdk.services.dynamodb.model.ExecuteStatementRequest; +import software.amazon.awssdk.services.dynamodb.model.ExecuteStatementResponse; +import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; +import software.amazon.awssdk.services.dynamodb.model.KeyType; +import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; +import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; +import software.amazon.awssdk.services.dynamodb.model.ScanRequest; +import software.amazon.awssdk.services.dynamodb.model.Select; +import software.amazon.awssdk.services.dynamodb.model.StreamSpecification; +import software.amazon.awssdk.services.dynamodb.model.TimeToLiveStatus; +import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; + +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static java.util.Objects.requireNonNull; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.entityAttributeName; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toAttributeValue; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toDocumentEntity; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItem; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItemUpdate; + +public class DefaultDynamoDBDocumentManager implements DynamoDBDocumentManager { + + private final String database; + + private final Settings settings; + + private final DynamoDbClient dynamoDbClient; + + private final ConcurrentHashMap> ttlAttributeNamesByTable = new ConcurrentHashMap<>(); + + private final ConcurrentHashMap tables = new ConcurrentHashMap<>(); + + public DefaultDynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient, Settings settings) { + this.settings = settings; + this.database = database; + this.dynamoDbClient = dynamoDbClient; + } + + private String resolveEntityNameAttributeName(String entityName) { + return this.settings.get(DynamoDBConfigurations.ENTITY_PARTITION_KEY, String.class).orElse(entityName); + } + + public DynamoDbClient dynamoDbClient() { + return dynamoDbClient; + } + + @Override + public String name() { + return database; + } + + @Override + public DocumentEntity insert(DocumentEntity documentEntity) { + requireNonNull(documentEntity, "documentEntity is required"); + dynamoDbClient().putItem(PutItemRequest.builder() + .tableName(createTableIfNeeded(documentEntity.name()).table().tableName()) + .item(toItem(this::resolveEntityNameAttributeName, documentEntity)) + .build()); + return documentEntity; + } + + private Supplier getTTLAttributeName(String tableName) { + return this.ttlAttributeNamesByTable.computeIfAbsent(tableName, this::getTTLAttributeNameSupplier); + } + + private Supplier getTTLAttributeNameSupplier(String tableName) { + createTableIfNeeded(tableName); + DescribeTimeToLiveResponse describeTimeToLiveResponse = dynamoDbClient().describeTimeToLive(DescribeTimeToLiveRequest.builder() + .tableName(tableName).build()); + if (TimeToLiveStatus.ENABLED.equals(describeTimeToLiveResponse.timeToLiveDescription().timeToLiveStatus())) { + var ttlAttributeName = describeTimeToLiveResponse.timeToLiveDescription().attributeName(); + return () -> ttlAttributeName; + } + return () -> tableName + " don't support TTL operations. Check if TTL support is enabled for this table."; + } + + private DescribeTableResponse createTableIfNeeded(String tableName) { + return this.tables.computeIfAbsent(tableName, this::resolveTable); + } + + private DescribeTableResponse resolveTable(String tableName) { + try { + return getDescribeTableResponse(tableName); + } catch (ResourceNotFoundException ex) { + if (!shouldCreateTables()) + throw ex; + return createTable(tableName); + } + } + + private DescribeTableResponse getDescribeTableResponse(String tableName) { + return dynamoDbClient().describeTable(DescribeTableRequest.builder() + .tableName(tableName) + .build()); + } + + private DescribeTableResponse createTable(String tableName) { + try (var waiter = dynamoDbClient().waiter()) { + dynamoDbClient().createTable(CreateTableRequest.builder() + .tableName(tableName) + .keySchema(defaultKeySchemaFor(tableName)) + .attributeDefinitions(defaultAttributeDefinitionsFor(tableName)) + .provisionedThroughput(defaultProvisionedThroughputFor(tableName)) + .streamSpecification(defaultStreamSpecificationFor(tableName)) + .build()); + + var tableRequest = DescribeTableRequest.builder().tableName(tableName).build(); + var waiterResponse = waiter.waitUntilTableExists(tableRequest); + return waiterResponse.matched().response().orElseThrow(); + } + } + + private StreamSpecification defaultStreamSpecificationFor(String tableName) { + return null; + } + + private ProvisionedThroughput defaultProvisionedThroughputFor(String tableName) { + return DynamoTableUtils.createProvisionedThroughput(null, null); + } + + private Collection defaultAttributeDefinitionsFor(String tableName) { + return List.of( + AttributeDefinition.builder().attributeName(getEntityAttributeName()).attributeType(ScalarAttributeType.S).build(), + AttributeDefinition.builder().attributeName(DocumentEntityConverter.ID).attributeType(ScalarAttributeType.S).build() + ); + } + + private Collection defaultKeySchemaFor(String tableName) { + return List.of( + KeySchemaElement.builder().attributeName(getEntityAttributeName()).keyType(KeyType.HASH).build(), + KeySchemaElement.builder().attributeName(DocumentEntityConverter.ID).keyType(KeyType.RANGE).build() + ); + } + + private boolean shouldCreateTables() { + return this.settings + .get(DynamoDBConfigurations.CREATE_TABLES, Boolean.class) + .orElse(false); + } + + private String getEntityAttributeName() { + return entityAttributeName(this::resolveEntityNameAttributeName); + } + + @Override + public DocumentEntity insert(DocumentEntity documentEntity, Duration ttl) { + requireNonNull(documentEntity, "documentEntity is required"); + requireNonNull(ttl, "ttl is required"); + documentEntity.add(getTTLAttributeName(documentEntity.name()).get(), Instant.now().plus(ttl).truncatedTo(ChronoUnit.SECONDS)); + return insert(documentEntity); + } + + @Override + public Iterable insert(Iterable entities) { + requireNonNull(entities, "entities are required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(this::insert) + .toList(); + } + + @Override + public Iterable insert(Iterable entities, Duration ttl) { + requireNonNull(entities, "entities is required"); + requireNonNull(ttl, "ttl is required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(e -> this.insert(e, ttl)) + .toList(); + } + + @Override + public DocumentEntity update(DocumentEntity documentEntity) { + requireNonNull(documentEntity, "entity is required"); + Map itemKey = getItemKey(documentEntity); + Map attributeUpdates = asItemToUpdate(documentEntity); + itemKey.keySet().forEach(attributeUpdates::remove); + dynamoDbClient().updateItem(UpdateItemRequest.builder() + .tableName(createTableIfNeeded(documentEntity.name()).table().tableName()) + .key(itemKey) + .attributeUpdates(attributeUpdates) + .build()); + return documentEntity; + } + + private Map getItemKey(DocumentEntity documentEntity) { + DescribeTableResponse describeTableResponse = this.tables.computeIfAbsent(documentEntity.name(), this::getDescribeTableResponse); + Map itemKey = describeTableResponse + .table() + .keySchema() + .stream() + .map(attribute -> Map.of(attribute.attributeName(), + toAttributeValue(documentEntity.find(attribute.attributeName(), Object.class).orElse(null)))) + .reduce(new HashMap<>(), (a, b) -> { + a.putAll(b); + return a; + }); + itemKey.put(getEntityAttributeName(), toAttributeValue(documentEntity.name())); + return itemKey; + } + + private Map asItemToUpdate(DocumentEntity documentEntity) { + return toItemUpdate(this::resolveEntityNameAttributeName, documentEntity); + } + + @Override + public Iterable update(Iterable entities) { + requireNonNull(entities, "entities is required"); + return StreamSupport.stream(entities.spliterator(), false) + .map(this::update) + .toList(); + } + + @Override + public void delete(DocumentDeleteQuery documentDeleteQuery) { + Objects.requireNonNull(documentDeleteQuery, "documentDeleteQuery is required"); + + List primaryKeys = getDescribeTableResponse(documentDeleteQuery.name()) + .table() + .keySchema() + .stream() + .map(KeySchemaElement::attributeName).toList(); + + DocumentQuery.DocumentQueryBuilder selectQueryBuilder = DocumentQuery.builder() + .select(primaryKeys.toArray(new String[0])) + .from(documentDeleteQuery.name()); + + documentDeleteQuery.condition().ifPresent(selectQueryBuilder::where); + + select(selectQueryBuilder.build()).forEach( + documentEntity -> + dynamoDbClient().deleteItem(DeleteItemRequest.builder() + .tableName(documentDeleteQuery.name()) + .key(getItemKey(documentEntity)) + .build())); + } + + @Override + public Stream select(DocumentQuery documentQuery) { + Objects.requireNonNull(documentQuery, "documentQuery is required"); + DynamoDBQuery dynamoDBQuery = DynamoDBQuery + .builderOf(documentQuery.name(), getEntityAttributeName(), documentQuery) + .get(); + + ScanRequest.Builder selectRequest = ScanRequest.builder() + .consistentRead(true) + .tableName(dynamoDBQuery.table()) + .projectionExpression(dynamoDBQuery.projectionExpression()) + .filterExpression(dynamoDBQuery.filterExpression()) + .expressionAttributeNames(dynamoDBQuery.expressionAttributeNames()) + .expressionAttributeValues(dynamoDBQuery.expressionAttributeValues()) + .select(dynamoDBQuery.projectionExpression() != null ? Select.SPECIFIC_ATTRIBUTES : Select.ALL_ATTRIBUTES); + + return StreamSupport + .stream(dynamoDbClient().scanPaginator(selectRequest.build()).spliterator(), false) + .flatMap(scanResponse -> scanResponse.items().stream() + .map(item -> toDocumentEntity(this::resolveEntityNameAttributeName, item))); + } + + @Override + public long count(String tableName) { + Objects.requireNonNull(tableName, "tableName is required"); + try { + return getDescribeTableResponse(tableName) + .table() + .itemCount(); + } catch (ResourceNotFoundException ex) { + return 0; + } + } + + @Override + public void close() { + this.dynamoDbClient.close(); + } + + @Override + public Stream partiQL(String query) { + return partiQL(query, new Object[0]); + } + + @Override + public Stream partiQL(String query, Object... params) { + Objects.requireNonNull(query, "query is required"); + List parameters = Stream.of(params).map(DocumentEntityConverter::toAttributeValue).toList(); + ExecuteStatementResponse executeStatementResponse = dynamoDbClient() + .executeStatement(ExecuteStatementRequest.builder() + .statement(query) + .parameters(parameters) + .build()); + List result = new LinkedList<>(); + executeStatementResponse.items().forEach(item -> result.add(toDocumentEntity(this::resolveEntityNameAttributeName, item))); + while (executeStatementResponse.nextToken() != null) { + executeStatementResponse = dynamoDbClient() + .executeStatement(ExecuteStatementRequest.builder() + .statement(query) + .parameters(parameters) + .build()); + executeStatementResponse.items().forEach(item -> result.add(toDocumentEntity(this::resolveEntityNameAttributeName, item))); + } + return result.stream(); + } +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java index 2a31a2c1c..1120783de 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java @@ -42,7 +42,7 @@ class DocumentEntityConverter { static final String ENTITY = "@entity"; - static final String ID = "_id"; + static final String ID = "id"; private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); private DocumentEntityConverter() { diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java index 6c9313324..52b34df75 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2024 Contributors to the Eclipse Foundation * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. @@ -15,268 +15,44 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.document.DocumentManager; -import org.eclipse.jnosql.communication.document.DocumentQuery; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse; -import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTimeToLiveResponse; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.model.StreamSpecification; -import software.amazon.awssdk.services.dynamodb.model.TimeToLiveStatus; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; import java.util.stream.Stream; -import java.util.stream.StreamSupport; -import static java.util.Objects.requireNonNull; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.entityAttributeName; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toAttributeValue; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItem; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItemUpdate; - -public class DynamoDBDocumentManager implements DocumentManager { - - private final String database; - - private final Settings settings; - - private final DynamoDbClient dynamoDbClient; - - private final ConcurrentHashMap> ttlAttributeNamesByTable = new ConcurrentHashMap<>(); - - private final ConcurrentHashMap tables = new ConcurrentHashMap<>(); - - public DynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient, Settings settings) { - this.settings = settings; - this.database = database; - this.dynamoDbClient = dynamoDbClient; - } - - private String resolveEntityNameAttributeName(String entityName) { - return this.settings.get(DynamoDBConfigurations.ENTITY_PARTITION_KEY, String.class).orElse(entityName); - } - - public DynamoDbClient getDynamoDbClient() { - return dynamoDbClient; - } - - @Override - public String name() { - return database; - } - - @Override - public DocumentEntity insert(DocumentEntity documentEntity) { - requireNonNull(documentEntity, "documentEntity is required"); - getDynamoDbClient().putItem(PutItemRequest.builder() - .tableName(createIfNeeded(documentEntity.name()).table().tableName()) - .item(toItem(this::resolveEntityNameAttributeName, documentEntity)) - .build()); - return documentEntity; - } - - private Supplier getTTLAttributeNameFor(String tableName) { - return this.ttlAttributeNamesByTable.computeIfAbsent(tableName, this::getTTLAttributeNameSupplierFromTable); - } - - private Supplier getTTLAttributeNameSupplierFromTable(String tableName) { - createIfNeeded(tableName); - DescribeTimeToLiveResponse describeTimeToLiveResponse = getDynamoDbClient().describeTimeToLive(DescribeTimeToLiveRequest.builder() - .tableName(tableName).build()); - if (TimeToLiveStatus.ENABLED.equals(describeTimeToLiveResponse.timeToLiveDescription().timeToLiveStatus())) { - var ttlAttributeName = describeTimeToLiveResponse.timeToLiveDescription().attributeName(); - return () -> ttlAttributeName; - } - return unsupportedTTLSupplierFor(tableName); - } - - private Supplier unsupportedTTLSupplierFor(String tableName) { - return () -> tableName + " don't support TTL operations. Check if TTL support is enabled for this table."; - } - - private DescribeTableResponse createIfNeeded(String tableName) { - return this.tables.computeIfAbsent(tableName, this::resolveTable); - } - - private DescribeTableResponse resolveTable(String tableName) { - try { - return getDescribeTableResponse(tableName); - } catch (ResourceNotFoundException ex) { - if (!shouldCreateTables()) - throw ex; - return createTable(tableName); - } - } - - private DescribeTableResponse getDescribeTableResponse(String tableName) { - return getDynamoDbClient().describeTable(DescribeTableRequest.builder() - .tableName(tableName) - .build()); - } - - private DescribeTableResponse createTable(String tableName) { - try (var waiter = getDynamoDbClient().waiter()) { - getDynamoDbClient().createTable(CreateTableRequest.builder() - .tableName(tableName) - .keySchema(defaultKeySchemaFor(tableName)) - .attributeDefinitions(defaultAttributeDefinitionsFor(tableName)) - .provisionedThroughput(defaultProvisionedThroughputFor(tableName)) - .streamSpecification(defaultStreamSpecificationFor(tableName)) - .build()); - - var tableRequest = DescribeTableRequest.builder().tableName(tableName).build(); - var waiterResponse = waiter.waitUntilTableExists(tableRequest); - return waiterResponse.matched().response().orElseThrow(); - } - } - - private StreamSpecification defaultStreamSpecificationFor(String tableName) { - return null; - } - - private ProvisionedThroughput defaultProvisionedThroughputFor(String tableName) { - return DynamoTableUtils.createProvisionedThroughput(null, null); - } - - private Collection defaultAttributeDefinitionsFor(String tableName) { - return List.of( - AttributeDefinition.builder().attributeName(getEntityAttributeName()).attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName(DocumentEntityConverter.ID).attributeType(ScalarAttributeType.S).build() - ); - } - - private Collection defaultKeySchemaFor(String tableName) { - return List.of( - KeySchemaElement.builder().attributeName(getEntityAttributeName()).keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName(DocumentEntityConverter.ID).keyType(KeyType.RANGE).build() - ); - } - - private boolean shouldCreateTables() { - return this.settings - .get(DynamoDBConfigurations.CREATE_TABLES, Boolean.class) - .orElse(false); - } - - private String getEntityAttributeName() { - return entityAttributeName(this::resolveEntityNameAttributeName); - } - - @Override - public DocumentEntity insert(DocumentEntity documentEntity, Duration ttl) { - requireNonNull(documentEntity, "documentEntity is required"); - requireNonNull(ttl, "ttl is required"); - documentEntity.add(getTTLAttributeNameFor(documentEntity.name()).get(), Instant.now().plus(ttl).truncatedTo(ChronoUnit.SECONDS)); - return insert(documentEntity); - } - - @Override - public Iterable insert(Iterable entities) { - requireNonNull(entities, "entities are required"); - return StreamSupport.stream(entities.spliterator(), false) - .map(this::insert) - .toList(); - } - - @Override - public Iterable insert(Iterable entities, Duration ttl) { - requireNonNull(entities, "entities is required"); - requireNonNull(ttl, "ttl is required"); - return StreamSupport.stream(entities.spliterator(), false) - .map(e -> this.insert(e, ttl)) - .toList(); - } - - @Override - public DocumentEntity update(DocumentEntity documentEntity) { - requireNonNull(documentEntity, "entity is required"); - Map itemKey = getItemKey(documentEntity); - Map attributeUpdates = asItemToUpdate(documentEntity); - itemKey.keySet().forEach(attributeUpdates::remove); - getDynamoDbClient().updateItem(UpdateItemRequest.builder() - .tableName(createIfNeeded(documentEntity.name()).table().tableName()) - .key(itemKey) - .attributeUpdates(attributeUpdates) - .build()); - return documentEntity; - } - - private Map getItemKey(DocumentEntity documentEntity) { - DescribeTableResponse describeTableResponse = this.tables.computeIfAbsent(documentEntity.name(), this::getDescribeTableResponse); - Map itemKey = describeTableResponse - .table() - .keySchema() - .stream() - .map(attribute -> Map.of(attribute.attributeName(), - toAttributeValue(documentEntity.find(attribute.attributeName(), Object.class).orElse(null)))) - .reduce(new HashMap<>(), (a, b) -> { - a.putAll(b); - return a; - }); - itemKey.put(getEntityAttributeName(), toAttributeValue(documentEntity.name())); - return itemKey; - } - - private Map asItemToUpdate(DocumentEntity documentEntity) { - return toItemUpdate(this::resolveEntityNameAttributeName, documentEntity); - } - - @Override - public Iterable update(Iterable entities) { - requireNonNull(entities, "entities is required"); - return StreamSupport.stream(entities.spliterator(), false) - .map(this::update) - .toList(); - } - - @Override - public void delete(DocumentDeleteQuery documentDeleteQuery) { - throw new UnsupportedOperationException("delete method must be implemented!"); - } - - @Override - public Stream select(DocumentQuery documentQuery) { - throw new UnsupportedOperationException("select method must be implemented!"); - } +/** + * A document manager interface for DynamoDB database operations. + */ +public interface DynamoDBDocumentManager extends DocumentManager { + + /** + * DynamoDB supports a limited subset of + * PartiQL. + * This method executes a PartiQL query and returns a stream of DocumentEntity objects. + * + * @param query the PartiQL query + * @return a {@link Stream} of {@link DocumentEntity} representing the query result + * @throws NullPointerException when the query is null + */ + Stream partiQL(String query); + + /** + * DynamoDB supports a limited subset of PartiQL. + * This method executes a PartiQL query with parameters and returns a stream of DocumentEntity objects. + *

Example query: {@code SELECT * FROM users WHERE status = ?}

+ * + * @param query the PartiQL query + * @return a {@link Stream} of {@link DocumentEntity} representing the query result + * @throws NullPointerException when the query is null + */ + Stream partiQL(String query, Object... params); + + + /** + * @return a {@link DynamoDbClient} instance for custom utilization + */ + DynamoDbClient dynamoDbClient(); - @Override - public long count(String tableName) { - Objects.requireNonNull(tableName, "tableName is required"); - try { - return getDescribeTableResponse(tableName) - .table() - .itemCount(); - } catch (ResourceNotFoundException ex) { - return 0; - } - } - @Override - public void close() { - this.dynamoDbClient.close(); - } } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java index d16c988ef..c101a2267 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java @@ -34,7 +34,7 @@ public DynamoDBDocumentManagerFactory(DynamoDbClient dynamoDB, Settings settings @Override public DocumentManager apply(String database) { - return new DynamoDBDocumentManager(database, dynamoDB, settings); + return new DefaultDynamoDBDocumentManager(database, dynamoDB, settings); } @Override diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuery.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuery.java new file mode 100644 index 000000000..26d544cd3 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuery.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.document.DocumentQuery; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.Map; +import java.util.function.Supplier; + +public record DynamoDBQuery(String table, + String projectionExpression, + String filterExpression, + Map expressionAttributeNames, + Map expressionAttributeValues) { + + public static Supplier builderOf(String table, + String partitionKey, + DocumentQuery documentQuery) { + return new DynamoDBQuerySelectBuilder(table, partitionKey, documentQuery); + } +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java new file mode 100644 index 000000000..12ad2312e --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentCondition; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toAttributeValue; + +abstract class DynamoDBQueryBuilder implements Supplier { + + protected void condition(DocumentCondition condition, + StringBuilder filterExpression, + Map expressionAttributeNames, + Map expressionAttributeValues) { + Document document = condition.document(); + + switch (condition.condition()) { + case EQUALS: + predicate(" = ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case LIKE: + predicateLike(document, filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case IN: + predicateIn(document, filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case GREATER_THAN: + predicate(" > ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case LESSER_THAN: + predicate(" < ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case GREATER_EQUALS_THAN: + predicate(" >= ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case LESSER_EQUALS_THAN: + predicate(" <= ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case BETWEEN: + predicateBetween(document, filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case AND: + appendCondition(document.get(new TypeReference<>() { + }), " AND ", filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case OR: + appendCondition(document.get(new TypeReference<>() { + }), " OR ", filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + case NOT: + filterExpression.append(" NOT "); + condition(document.get(DocumentCondition.class), filterExpression, expressionAttributeNames, expressionAttributeValues); + break; + default: + throw new IllegalArgumentException("Unknown condition " + condition.condition()); + } + } + + private void predicateIn(Document document, + StringBuilder filterExpression, + Map expressionAttributeNames, + Map expressionAttributeValues) { + var name = document.name(); + + var attributeName = "#" + name; + expressionAttributeNames.put(attributeName, name); + filterExpression.append(attributeName).append(" IN ("); + + List valuesExpressionNames = new LinkedList<>(); + ((Iterable) document.get()).forEach(value -> { + var attributeValueName = ":" + name + "_" + expressionAttributeValues.size(); + valuesExpressionNames.add(attributeValueName); + expressionAttributeValues.put(attributeValueName, toAttributeValue(value)); + }); + + filterExpression.append(String.join(", ", valuesExpressionNames)); + filterExpression.append(") "); + } + + private void appendCondition(List conditions, + String condition, + StringBuilder filterExpression, + Map expressionAttributeNames, + Map expressionAttributeValues) { + + boolean isFirstCondition = true; + for (DocumentCondition documentCondition : conditions) { + StringBuilder tempFilterExpression = new StringBuilder(); + HashMap tempExpressionAttributeNames = new HashMap<>(expressionAttributeNames); + HashMap tempExpressionAttributeValues = new HashMap<>(expressionAttributeValues); + condition(documentCondition, + tempFilterExpression, + tempExpressionAttributeNames, + tempExpressionAttributeValues); + if (isFirstCondition && !tempFilterExpression.isEmpty()) { + filterExpression.append(tempFilterExpression); + expressionAttributeNames.putAll(tempExpressionAttributeNames); + expressionAttributeValues.putAll(tempExpressionAttributeValues); + } else if (!tempFilterExpression.isEmpty()) { + if (!filterExpression.substring(filterExpression.length() - condition.length()).equals(condition)) { + filterExpression.append(condition); + } + filterExpression.append(tempFilterExpression); + expressionAttributeNames.putAll(tempExpressionAttributeNames); + expressionAttributeValues.putAll(tempExpressionAttributeValues); + } + isFirstCondition = false; + } + + + } + + private void predicateBetween(Document document, + StringBuilder filterExpression, + Map expressionAttributeNames, + Map expressionAttributeValues) { + + var name = document.name(); + + List values = new ArrayList<>(); + ((Iterable) document.get()).forEach(values::add); + + var attributeName = "#" + name; + expressionAttributeNames.put(attributeName, name); + + filterExpression.append(attributeName).append(" BETWEEN "); + + var fistAttributeValueName = ":" + name + "_" + expressionAttributeValues.size(); + expressionAttributeValues.put(fistAttributeValueName, toAttributeValue(values.get(0))); + filterExpression.append(fistAttributeValueName).append(" AND "); + + var secondAttributeValueName = ":" + name + "_" + expressionAttributeValues.size(); + expressionAttributeValues.put(secondAttributeValueName, toAttributeValue(values.get(1))); + filterExpression.append(secondAttributeValueName); + + } + + private void predicateLike(Document document, + StringBuilder filterExpression, + Map expressionAttributeNames, + Map expressionAttributeValues) { + + var name = document.name(); + var value = toAttributeValue(document.get()); + + var attributeName = "#" + name; + var attributeValueName = ":" + name + "_" + expressionAttributeValues.size(); + + filterExpression.append("begins_with(") + .append(attributeName).append(',') + .append(attributeValueName).append(')'); + + expressionAttributeNames.put(attributeName, name); + expressionAttributeValues.put(attributeValueName, value); + } + + protected void predicate(String operator, + Document document, + StringBuilder filterExpression, + Map expressionAttributeNames, + Map expressionAttributeValues) { + + var name = document.name(); + var value = toAttributeValue(document.get()); + + var attributeName = "#" + name; + var attributeValueName = ":" + name + "_" + expressionAttributeValues.size(); + + filterExpression.append(attributeName).append(operator).append(attributeValueName); + expressionAttributeNames.put(attributeName, name); + expressionAttributeValues.put(attributeValueName, value); + + } + +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuerySelectBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuerySelectBuilder.java new file mode 100644 index 000000000..48d4e1767 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuerySelectBuilder.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentCondition; +import org.eclipse.jnosql.communication.document.DocumentQuery; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.HashMap; + +class DynamoDBQuerySelectBuilder extends DynamoDBQueryBuilder { + + private final String table; + + private final String partitionKey; + + private final DocumentQuery documentQuery; + + public DynamoDBQuerySelectBuilder(String table, + String partitionKey, + DocumentQuery documentQuery) { + this.table = table; + this.partitionKey = partitionKey; + this.documentQuery = documentQuery; + } + + @Override + public DynamoDBQuery get() { + + var filterExpression = new StringBuilder(); + var expressionAttributeNames = new HashMap(); + var expressionAttributeValues = new HashMap(); + + super.condition( + DocumentCondition.eq(Document.of(partitionKey, documentQuery.name())), + filterExpression, expressionAttributeNames, expressionAttributeValues); + + this.documentQuery.condition().ifPresent(c -> { + filterExpression.append(" AND "); + super.condition(c, + filterExpression, + expressionAttributeNames, + expressionAttributeValues); + }); + + + return new DynamoDBQuery( + table, + projectionExpression(), + filterExpression.toString(), + expressionAttributeNames, + expressionAttributeValues); + } + + String projectionExpression() { + var documents = documentQuery.documents(); + if (documents.isEmpty()) { + return null; + } + return String.join(", ", documents); + } + + +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuery.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuery.java new file mode 100644 index 000000000..2412c7a3a --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuery.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; +import org.eclipse.jnosql.communication.document.DocumentQuery; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.List; +import java.util.function.Supplier; + +public record PartiQLQuery(String query, List params, Long limit, Long skip) { + static Supplier toPartiQLQuery(String table, String partitionKey, DocumentQuery documentQuery) { + return new PartiQLQuerySelectBuilder(table, partitionKey, documentQuery); + } + + static Supplier toPartiQLQuery(String table, String partitionKey, DocumentDeleteQuery documentDeleteQuery) { + return new PartiQLQueryDeleteBuilder(table, partitionKey, documentDeleteQuery); + } + +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryBuilder.java new file mode 100644 index 000000000..0469aafa2 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryBuilder.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.TypeReference; +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentCondition; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import static java.util.stream.Collectors.joining; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toAttributeValue; + +abstract class PartiQLQueryBuilder implements Supplier { + + protected void condition(DocumentCondition condition, + StringBuilder query, + List params) { + Document document = condition.document(); + + switch (condition.condition()) { + case EQUALS: + predicate(query, " = ", document, params); + break; + case LIKE: + predicateLike(query, document, params); + break; + case IN: + predicateIn(query, document, params); + break; + case GREATER_THAN: + predicate(query, " > ", document, params); + break; + case LESSER_THAN: + predicate(query, " >= ", document, params); + break; + case GREATER_EQUALS_THAN: + predicate(query, " < ", document, params); + break; + case LESSER_EQUALS_THAN: + predicate(query, " <= ", document, params); + break; + case BETWEEN: + predicateBetween(query, document, params); + break; + case AND: + appendCondition(query, params, document.get(new TypeReference<>() { + }), " AND "); + break; + case OR: + appendCondition(query, params, document.get(new TypeReference<>() { + }), " OR "); + break; + case NOT: + query.append(" NOT "); + condition(document.get(DocumentCondition.class), query, params); + break; + default: + throw new IllegalArgumentException("Unknown condition " + condition.condition()); + } + } + + private void predicateIn(StringBuilder query, Document document, List params) { + var name = document.name(); + List values = new ArrayList<>(); + ((Iterable) document.get()).forEach(values::add); + query.append(name).append(" IN (") + .append(values.stream().map(i->"?").collect(joining(","))) + .append(") "); + values.stream().map(DocumentEntityConverter::toAttributeValue).forEach(params::add); + } + + protected void predicateLike(StringBuilder query, Document document, List params) { + var name = document.name(); + var param = toAttributeValue(document.get()); + query.append("begins_with(").append(name).append(", ? )"); + params.add(param); + } + + protected void predicateBetween(StringBuilder query, Document document, List params) { + var name = document.name(); + List values = new ArrayList<>(); + ((Iterable) document.get()).forEach(values::add); + query.append(name).append(" BETWEEN ? AND ? "); + params.add(toAttributeValue(values.get(0))); + params.add(toAttributeValue(values.get(1))); + } + + protected void appendCondition(StringBuilder query, List params, List conditions, String condition) { + boolean isFirstCondition = true; + for (DocumentCondition documentCondition : conditions) { + StringBuilder appendQuery = new StringBuilder(); + condition(documentCondition, appendQuery, params); + if (isFirstCondition && !appendQuery.isEmpty()) { + query.append(appendQuery); + } else if (!appendQuery.isEmpty()) { + if (!query.substring(query.length() - condition.length()).equals(condition)) { + query.append(condition); + } + query.append(appendQuery); + } + isFirstCondition = false; + } + + } + + protected void predicate(StringBuilder query, String condition, Document document, List params) { + var name = document.name(); + var param = toAttributeValue(document.get()); + query.append(name).append(condition).append(" ? "); + params.add(param); + } +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryDeleteBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryDeleteBuilder.java new file mode 100644 index 000000000..2089d60c6 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryDeleteBuilder.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentCondition; +import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.LinkedList; +import java.util.List; + +public class PartiQLQueryDeleteBuilder extends PartiQLQueryBuilder { + + private final String table; + + private final String partitionKey; + + private final DocumentDeleteQuery documentDeleteQuery; + + public PartiQLQueryDeleteBuilder(String table, String partitionKey, DocumentDeleteQuery documentDeleteQuery) { + this.table = table; + this.partitionKey = partitionKey; + this.documentDeleteQuery = documentDeleteQuery; + } + + @Override + public PartiQLQuery get() { + + var query = new StringBuilder(); + List params = new LinkedList<>(); + + query.append("DELETE FROM ").append(table).append(" WHERE "); + condition(DocumentCondition.eq(Document.of(partitionKey, documentDeleteQuery.name())), query, params); + this.documentDeleteQuery.condition().ifPresent(c -> { + query.append(" AND "); + condition(c, query, params); + }); + + return new PartiQLQuery(query.toString(), params, null, null); + } +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuerySelectBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuerySelectBuilder.java new file mode 100644 index 000000000..eba37b74e --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuerySelectBuilder.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.communication; + +import jakarta.data.Direction; +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentCondition; +import org.eclipse.jnosql.communication.document.DocumentQuery; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class PartiQLQuerySelectBuilder extends PartiQLQueryBuilder { + private final String table; + private final String partitionKey; + private final DocumentQuery documentQuery; + + public PartiQLQuerySelectBuilder(String table, String partitionKey, DocumentQuery documentQuery) { + this.table = table; + this.partitionKey = partitionKey; + this.documentQuery = documentQuery; + } + + + @Override + public PartiQLQuery get() { + var query = new StringBuilder(); + List params = new LinkedList<>(); + + query.append("SELECT "); + query.append(select()).append(' '); + query.append("FROM ").append(table).append(' '); + query.append("WHERE "); + condition(DocumentCondition.eq(Document.of(partitionKey, documentQuery.name())), query, params); + this.documentQuery.condition().ifPresent(c -> { + query.append(" AND "); + condition(c, query, params); + }); + + if (!this.documentQuery.sorts().isEmpty()) { + query.append(" ORDER BY "); + var order = this.documentQuery.sorts().stream() + .map(s -> s.property() + " " + (s.isAscending() ? Direction.ASC : Direction.DESC)) + .collect(Collectors.joining(", ")); + query.append(order); + } + + return new PartiQLQuery(query.toString(), params, this.documentQuery.limit(), this.documentQuery.skip()); + } + + String select() { + var documents = documentQuery.documents(); + if (documents.isEmpty()){ + return "*"; + } + return partitionKey + "," + String.join(", ", documents); + } +} diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerFactoryTest.java similarity index 97% rename from jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java rename to jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerFactoryTest.java index 70941cc42..faf6a8cfa 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactoryTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerFactoryTest.java @@ -28,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) -class DynamoDBDocumentManagerFactoryTest { +class DefaultDynamoDBDocumentManagerFactoryTest { private DocumentManagerFactory documentManagerFactory; diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java similarity index 70% rename from jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java rename to jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java index b9383390a..48aa3565e 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java @@ -44,13 +44,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.eclipse.jnosql.communication.document.DocumentDeleteQuery.delete; +import static org.eclipse.jnosql.communication.document.DocumentQuery.select; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; +import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.ID; import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityGenerator.createRandomEntity; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils.CONFIG; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) -class DynamoDBDocumentManagerTest { +class DefaultDynamoDBDocumentManagerTest { private static Faker faker = new Faker(); @@ -164,7 +167,7 @@ void shouldInsert() { persistedEntities.forEach(entity -> { var _entityType = entity.name(); - var id = entity.find("_id", String.class).orElseThrow(); + var id = entity.find(ID, String.class).orElseThrow(); var persistedItem = getItem(_entityType, id); softly.assertThat(persistedItem) .as("all items of the list of DocumentEntity should be stored on dynamodb database. the entity %s not found" @@ -296,7 +299,155 @@ void shouldCountByCollectionName() { } } + @Test + void shouldDelete() { + try (var documentManager = getDocumentManagerCanCreateTables()) { + + DocumentEntity entity1, entity2, entity3, entity4; + + var entities = List.of( + entity1 = createRandomEntity(), + entity2 = createRandomEntity(), + entity3 = createRandomEntity(), + entity4 = createRandomEntity()); + + documentManager.insert(entities); + + var entityType = entity1.name(); + var id1 = entity1.find(ID, String.class).orElseThrow(); + var id2 = entity2.find(ID, String.class).orElseThrow(); + var id3 = entity3.find(ID, String.class).orElseThrow(); + var id4 = entity4.find(ID, String.class).orElseThrow(); + + assertSoftly(softly -> { + + documentManager.delete(delete(). + from(entityType) + .where(ID).eq(id1) + .build() + ); + + softly.assertThat(documentManager.count(entityType)) + .as("the returned count number of items from a given table name is incorrect") + .isEqualTo(entities.size() - 1L); + + softly.assertThat(getItem(entityType, id1)) + .as("the item should be deleted") + .hasSize(0); + + documentManager.delete(delete(). + from(entityType) + .where(ID).in(List.of(id2, id3)) + .build() + ); + + softly.assertThat(documentManager.count(entityType)) + .as("the returned count number of items from a given table name is incorrect") + .isEqualTo(entities.size() - 3L); + + softly.assertThat(getItem(entityType, id2)) + .as("the item should be deleted") + .hasSize(0); + + softly.assertThat(getItem(entityType, id3)) + .as("the item should be deleted") + .hasSize(0); + + + documentManager.delete(delete(). + from(entityType) + .build() + ); + + softly.assertThat(getItem(entityType, id4)) + .as("the item should be deleted") + .hasSize(0); + + softly.assertThat(documentManager.count(entityType)) + .as("the returned count number of items from a given table name is incorrect") + .isEqualTo(0); + + + }); + } + } + @Test void shouldCountByDocumentQuery() { + + try (var documentManager = getDocumentManagerCanCreateTables()) { + + DocumentEntity entity1, entity2, entity3; + + var entities = List.of(entity1 = createRandomEntity(), entity2 = createRandomEntity(), entity3 = createRandomEntity()); + + documentManager.insert(entities); + + assertSoftly(softly -> { + + var documentQuery1 = select() + .from(entity1.name()) + .where(ID).eq(entity1.find(ID, String.class).orElseThrow()) + .build(); + + softly.assertThat(documentManager.count(documentQuery1)) + .as("the returned count number of items from a given DocumentQuery is incorrect") + .isEqualTo(1L); + + + var documentQuery2 = select() + .from(entity1.name()) + .where(ID).eq(entity1.find(ID, String.class).orElseThrow()) + .or(ID).eq(entity2.find(ID, String.class).orElseThrow()) + .build(); + + softly.assertThat(documentManager.count(documentQuery2)) + .as("the returned count number of items from a given DocumentQuery is incorrect") + .isEqualTo(2L); + + var documentQuery3 = select() + .from(entity1.name()) + .where(ID).eq(entity1.find(ID, String.class).orElseThrow()) + .or(ID).eq(entity2.find(ID, String.class).orElseThrow()) + .or(ID).eq(entity3.find(ID, String.class).orElseThrow()) + .build(); + + softly.assertThat(documentManager.count(documentQuery3)) + .as("the returned count number of items from a given DocumentQuery is incorrect") + .isEqualTo(3L); + + }); + } + } + + @Test + void shouldExecutePartiQL() { + + try (var documentManager = getDocumentManagerCanCreateTables()) { + + DocumentEntity entity1; + var entities = List.of(entity1 = createRandomEntity(), createRandomEntity(), createRandomEntity()); + documentManager.insert(entities); + + if (documentManager instanceof DynamoDBDocumentManager partiManager) { + + assertSoftly(softly -> { + softly.assertThat(partiManager.partiQL("SELECT * FROM " + entity1.name())) + .as("the returned count number of items from a given DocumentQuery is incorrect") + .hasSize(3); + + }); + + assertSoftly(softly -> { + softly.assertThat(partiManager.partiQL(""" + SELECT * FROM %s WHERE %s = ? + """.formatted(entity1.name(), ID), + entity1.find(ID).orElseThrow().get())) + .as("the returned count number of items from a given DocumentQuery is incorrect") + .hasSize(1); + + }); + } + } } } diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java index 6405b3af3..f99a1d38b 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java @@ -50,13 +50,11 @@ static DocumentEntity createRandomEntityWithSubDocuments(int levels) { @NotNull private static DocumentEntity createRandomEntityWithSubDocuments(String collectionName, int levels) { Map map = new HashMap<>(); - map.put("_id", UUID.randomUUID().toString()); + map.put(DocumentEntityConverter.ID, UUID.randomUUID().toString()); map.put("name", faker.name().firstName()); map.put("hoje", LocalDate.now()); map.put("agora", LocalDateTime.now()); - map.put("integerNumber", faker.random().nextInt(1, 10)); - map.put("floatNumber", (float) faker.random().nextDouble(1.0, 10.0)); - map.put("doubleNumber", faker.random().nextDouble(1.0, 10.0)); + map.put("guessingNumber", faker.random().nextInt(1, 10)); map.put("bigdecimal", BigDecimal.valueOf(10.10)); map.put("city", faker.address().city()); map.put("texts", List.of("A", "B", "C")); From 12b4053fedbc402320f24245737e7f27d2f2ec43 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 28 Jan 2024 07:29:57 -0300 Subject: [PATCH 44/70] build: bump dynamodb version to 2.23.12 Signed-off-by: Maximillian Arruda --- jnosql-dynamodb/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnosql-dynamodb/pom.xml b/jnosql-dynamodb/pom.xml index b8ecf9c11..bc664215b 100644 --- a/jnosql-dynamodb/pom.xml +++ b/jnosql-dynamodb/pom.xml @@ -22,7 +22,7 @@ The Eclipse JNoSQL layer implementation AWS DynamoDB - 2.21.21 + 2.23.12 From 28b4e92c887e46526c6d4a371b330c84dc90220c Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 28 Jan 2024 23:39:41 -0300 Subject: [PATCH 45/70] chore: removed unnecessary field Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DocumentEntityConverter.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java index 1120783de..2b3f4efce 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java @@ -15,10 +15,8 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import jakarta.json.bind.Jsonb; import org.eclipse.jnosql.communication.document.Document; import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.communication.driver.JsonbSupplier; import org.eclipse.jnosql.communication.driver.ValueUtil; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.dynamodb.model.AttributeAction; @@ -43,7 +41,6 @@ class DocumentEntityConverter { static final String ENTITY = "@entity"; static final String ID = "id"; - private static final Jsonb JSONB = JsonbSupplier.getInstance().get(); private DocumentEntityConverter() { } From d801b44c2c1b1669743e4d13e09d3a12617ade5d Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 28 Jan 2024 23:40:49 -0300 Subject: [PATCH 46/70] chore: passing next token to the partiql executions Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DefaultDynamoDBDocumentManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java index ba9626935..8d47bcde4 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java @@ -339,6 +339,7 @@ public Stream partiQL(String query, Object... params) { .executeStatement(ExecuteStatementRequest.builder() .statement(query) .parameters(parameters) + .nextToken(executeStatementResponse.nextToken()) .build()); executeStatementResponse.items().forEach(item -> result.add(toDocumentEntity(this::resolveEntityNameAttributeName, item))); } From c6bac72d8a5a45058ded4677994d778ee99c4daa Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:12:59 -0300 Subject: [PATCH 47/70] chore: removed unnecessary classes Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/PartiQLQuery.java | 34 ----- .../communication/PartiQLQueryBuilder.java | 129 ------------------ .../PartiQLQueryDeleteBuilder.java | 55 -------- .../PartiQLQuerySelectBuilder.java | 73 ---------- 4 files changed, 291 deletions(-) delete mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuery.java delete mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryBuilder.java delete mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryDeleteBuilder.java delete mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuerySelectBuilder.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuery.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuery.java deleted file mode 100644 index 2412c7a3a..000000000 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuery.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Apache License v2.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. - * - * You may elect to redistribute this code under either of these licenses. - * - * Contributors: - * - * Maximillian Arruda - */ - -package org.eclipse.jnosql.databases.dynamodb.communication; - -import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; -import org.eclipse.jnosql.communication.document.DocumentQuery; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -import java.util.List; -import java.util.function.Supplier; - -public record PartiQLQuery(String query, List params, Long limit, Long skip) { - static Supplier toPartiQLQuery(String table, String partitionKey, DocumentQuery documentQuery) { - return new PartiQLQuerySelectBuilder(table, partitionKey, documentQuery); - } - - static Supplier toPartiQLQuery(String table, String partitionKey, DocumentDeleteQuery documentDeleteQuery) { - return new PartiQLQueryDeleteBuilder(table, partitionKey, documentDeleteQuery); - } - -} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryBuilder.java deleted file mode 100644 index 0469aafa2..000000000 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryBuilder.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Apache License v2.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. - * - * You may elect to redistribute this code under either of these licenses. - * - * Contributors: - * - * Maximillian Arruda - */ - -package org.eclipse.jnosql.databases.dynamodb.communication; - -import org.eclipse.jnosql.communication.TypeReference; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentCondition; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -import static java.util.stream.Collectors.joining; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toAttributeValue; - -abstract class PartiQLQueryBuilder implements Supplier { - - protected void condition(DocumentCondition condition, - StringBuilder query, - List params) { - Document document = condition.document(); - - switch (condition.condition()) { - case EQUALS: - predicate(query, " = ", document, params); - break; - case LIKE: - predicateLike(query, document, params); - break; - case IN: - predicateIn(query, document, params); - break; - case GREATER_THAN: - predicate(query, " > ", document, params); - break; - case LESSER_THAN: - predicate(query, " >= ", document, params); - break; - case GREATER_EQUALS_THAN: - predicate(query, " < ", document, params); - break; - case LESSER_EQUALS_THAN: - predicate(query, " <= ", document, params); - break; - case BETWEEN: - predicateBetween(query, document, params); - break; - case AND: - appendCondition(query, params, document.get(new TypeReference<>() { - }), " AND "); - break; - case OR: - appendCondition(query, params, document.get(new TypeReference<>() { - }), " OR "); - break; - case NOT: - query.append(" NOT "); - condition(document.get(DocumentCondition.class), query, params); - break; - default: - throw new IllegalArgumentException("Unknown condition " + condition.condition()); - } - } - - private void predicateIn(StringBuilder query, Document document, List params) { - var name = document.name(); - List values = new ArrayList<>(); - ((Iterable) document.get()).forEach(values::add); - query.append(name).append(" IN (") - .append(values.stream().map(i->"?").collect(joining(","))) - .append(") "); - values.stream().map(DocumentEntityConverter::toAttributeValue).forEach(params::add); - } - - protected void predicateLike(StringBuilder query, Document document, List params) { - var name = document.name(); - var param = toAttributeValue(document.get()); - query.append("begins_with(").append(name).append(", ? )"); - params.add(param); - } - - protected void predicateBetween(StringBuilder query, Document document, List params) { - var name = document.name(); - List values = new ArrayList<>(); - ((Iterable) document.get()).forEach(values::add); - query.append(name).append(" BETWEEN ? AND ? "); - params.add(toAttributeValue(values.get(0))); - params.add(toAttributeValue(values.get(1))); - } - - protected void appendCondition(StringBuilder query, List params, List conditions, String condition) { - boolean isFirstCondition = true; - for (DocumentCondition documentCondition : conditions) { - StringBuilder appendQuery = new StringBuilder(); - condition(documentCondition, appendQuery, params); - if (isFirstCondition && !appendQuery.isEmpty()) { - query.append(appendQuery); - } else if (!appendQuery.isEmpty()) { - if (!query.substring(query.length() - condition.length()).equals(condition)) { - query.append(condition); - } - query.append(appendQuery); - } - isFirstCondition = false; - } - - } - - protected void predicate(StringBuilder query, String condition, Document document, List params) { - var name = document.name(); - var param = toAttributeValue(document.get()); - query.append(name).append(condition).append(" ? "); - params.add(param); - } -} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryDeleteBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryDeleteBuilder.java deleted file mode 100644 index 2089d60c6..000000000 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQueryDeleteBuilder.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Apache License v2.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. - * - * You may elect to redistribute this code under either of these licenses. - * - * Contributors: - * - * Maximillian Arruda - */ - -package org.eclipse.jnosql.databases.dynamodb.communication; - -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentCondition; -import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -import java.util.LinkedList; -import java.util.List; - -public class PartiQLQueryDeleteBuilder extends PartiQLQueryBuilder { - - private final String table; - - private final String partitionKey; - - private final DocumentDeleteQuery documentDeleteQuery; - - public PartiQLQueryDeleteBuilder(String table, String partitionKey, DocumentDeleteQuery documentDeleteQuery) { - this.table = table; - this.partitionKey = partitionKey; - this.documentDeleteQuery = documentDeleteQuery; - } - - @Override - public PartiQLQuery get() { - - var query = new StringBuilder(); - List params = new LinkedList<>(); - - query.append("DELETE FROM ").append(table).append(" WHERE "); - condition(DocumentCondition.eq(Document.of(partitionKey, documentDeleteQuery.name())), query, params); - this.documentDeleteQuery.condition().ifPresent(c -> { - query.append(" AND "); - condition(c, query, params); - }); - - return new PartiQLQuery(query.toString(), params, null, null); - } -} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuerySelectBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuerySelectBuilder.java deleted file mode 100644 index eba37b74e..000000000 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/PartiQLQuerySelectBuilder.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Apache License v2.0 which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. - * - * You may elect to redistribute this code under either of these licenses. - * - * Contributors: - * - * Maximillian Arruda - */ - -package org.eclipse.jnosql.databases.dynamodb.communication; - -import jakarta.data.Direction; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentCondition; -import org.eclipse.jnosql.communication.document.DocumentQuery; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -public class PartiQLQuerySelectBuilder extends PartiQLQueryBuilder { - private final String table; - private final String partitionKey; - private final DocumentQuery documentQuery; - - public PartiQLQuerySelectBuilder(String table, String partitionKey, DocumentQuery documentQuery) { - this.table = table; - this.partitionKey = partitionKey; - this.documentQuery = documentQuery; - } - - - @Override - public PartiQLQuery get() { - var query = new StringBuilder(); - List params = new LinkedList<>(); - - query.append("SELECT "); - query.append(select()).append(' '); - query.append("FROM ").append(table).append(' '); - query.append("WHERE "); - condition(DocumentCondition.eq(Document.of(partitionKey, documentQuery.name())), query, params); - this.documentQuery.condition().ifPresent(c -> { - query.append(" AND "); - condition(c, query, params); - }); - - if (!this.documentQuery.sorts().isEmpty()) { - query.append(" ORDER BY "); - var order = this.documentQuery.sorts().stream() - .map(s -> s.property() + " " + (s.isAscending() ? Direction.ASC : Direction.DESC)) - .collect(Collectors.joining(", ")); - query.append(order); - } - - return new PartiQLQuery(query.toString(), params, this.documentQuery.limit(), this.documentQuery.skip()); - } - - String select() { - var documents = documentQuery.documents(); - if (documents.isEmpty()){ - return "*"; - } - return partitionKey + "," + String.join(", ", documents); - } -} From 4d4f24c38a1423a172ac9022da5ad360f9aa5ee2 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:21:51 -0300 Subject: [PATCH 48/70] chore: renamed class and imports from into their dependents Signed-off-by: Maximillian Arruda --- .../DefaultDynamoDBDocumentManager.java | 34 +++++++++---------- ...yConverter.java => DynamoDBConverter.java} | 16 ++++----- .../communication/DynamoDBQueryBuilder.java | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) rename jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/{DocumentEntityConverter.java => DynamoDBConverter.java} (93%) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java index 8d47bcde4..9e235e2e4 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java @@ -58,11 +58,11 @@ import java.util.stream.StreamSupport; import static java.util.Objects.requireNonNull; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.entityAttributeName; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toAttributeValue; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toDocumentEntity; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItem; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toItemUpdate; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.entityAttributeName; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toAttributeValue; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toDocumentEntity; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toItem; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toItemUpdate; public class DefaultDynamoDBDocumentManager implements DynamoDBDocumentManager { @@ -144,10 +144,10 @@ private DescribeTableResponse createTable(String tableName) { try (var waiter = dynamoDbClient().waiter()) { dynamoDbClient().createTable(CreateTableRequest.builder() .tableName(tableName) - .keySchema(defaultKeySchemaFor(tableName)) - .attributeDefinitions(defaultAttributeDefinitionsFor(tableName)) - .provisionedThroughput(defaultProvisionedThroughputFor(tableName)) - .streamSpecification(defaultStreamSpecificationFor(tableName)) + .keySchema(defaultKeySchemaFor()) + .attributeDefinitions(defaultAttributeDefinitionsFor()) + .provisionedThroughput(defaultProvisionedThroughputFor()) + .streamSpecification(defaultStreamSpecificationFor()) .build()); var tableRequest = DescribeTableRequest.builder().tableName(tableName).build(); @@ -156,25 +156,25 @@ private DescribeTableResponse createTable(String tableName) { } } - private StreamSpecification defaultStreamSpecificationFor(String tableName) { + private StreamSpecification defaultStreamSpecificationFor() { return null; } - private ProvisionedThroughput defaultProvisionedThroughputFor(String tableName) { + private ProvisionedThroughput defaultProvisionedThroughputFor() { return DynamoTableUtils.createProvisionedThroughput(null, null); } - private Collection defaultAttributeDefinitionsFor(String tableName) { + private Collection defaultAttributeDefinitionsFor() { return List.of( AttributeDefinition.builder().attributeName(getEntityAttributeName()).attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName(DocumentEntityConverter.ID).attributeType(ScalarAttributeType.S).build() + AttributeDefinition.builder().attributeName(DynamoDBConverter.ID).attributeType(ScalarAttributeType.S).build() ); } - private Collection defaultKeySchemaFor(String tableName) { + private Collection defaultKeySchemaFor() { return List.of( KeySchemaElement.builder().attributeName(getEntityAttributeName()).keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName(DocumentEntityConverter.ID).keyType(KeyType.RANGE).build() + KeySchemaElement.builder().attributeName(DynamoDBConverter.ID).keyType(KeyType.RANGE).build() ); } @@ -320,13 +320,13 @@ public void close() { @Override public Stream partiQL(String query) { - return partiQL(query, new Object[0]); + return partiQL(query,new Object[0]); } @Override public Stream partiQL(String query, Object... params) { Objects.requireNonNull(query, "query is required"); - List parameters = Stream.of(params).map(DocumentEntityConverter::toAttributeValue).toList(); + List parameters = Stream.of(params).map(DynamoDBConverter::toAttributeValue).toList(); ExecuteStatementResponse executeStatementResponse = dynamoDbClient() .executeStatement(ExecuteStatementRequest.builder() .statement(query) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java similarity index 93% rename from jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java rename to jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java index 2b3f4efce..01ed968db 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java @@ -37,12 +37,12 @@ import static java.util.Collections.singletonMap; import static java.util.stream.Collectors.toList; -class DocumentEntityConverter { +class DynamoDBConverter { static final String ENTITY = "@entity"; static final String ID = "id"; - private DocumentEntityConverter() { + private DynamoDBConverter() { } private static Object convertValue(Object value) { @@ -61,7 +61,7 @@ private static Object convertValue(Object value) { case BS: return attributeValue.bs().stream().map(SdkBytes::asByteArray).toList(); case L: - return attributeValue.l().stream().map(DocumentEntityConverter::convertValue).toList(); + return attributeValue.l().stream().map(DynamoDBConverter::convertValue).toList(); case M: return attributeValue.m().entrySet().stream().map(e -> Document.of(e.getKey(), convertValue(e.getValue()))).toList(); case NUL: @@ -88,18 +88,18 @@ public static String entityAttributeName(UnaryOperator nameResolver) { return Optional.ofNullable(nameResolver.apply(ENTITY)).orElse(ENTITY); } + @SuppressWarnings({"rawtypes", "unchecked"}) private static Consumer feedJSON(Map jsonObject) { return d -> { Object value = ValueUtil.convert(d.value()); - if (value instanceof Document) { - Document subDocument = Document.class.cast(value); + if (value instanceof Document subDocument) { jsonObject.put(d.name(), singletonMap(subDocument.name(), subDocument.get())); } else if (isSudDocument(value)) { Map subDocument = getMap(value); jsonObject.put(d.name(), subDocument); } else if (isSudDocumentList(value)) { - jsonObject.put(d.name(), StreamSupport.stream(Iterable.class.cast(value).spliterator(), false) - .map(DocumentEntityConverter::getMap).collect(toList())); + jsonObject.put(d.name(), StreamSupport.stream(((Iterable) value).spliterator(), false) + .map(DynamoDBConverter::getMap).toList()); } else { jsonObject.put(d.name(), value); } @@ -147,7 +147,7 @@ public static AttributeValue toAttributeValue(Object value) { return AttributeValue.builder().bool(bool).build(); if (value instanceof List list) return AttributeValue.builder().l(list.stream().filter(Objects::nonNull) - .map(DocumentEntityConverter::toAttributeValue).toList()).build(); + .map(DynamoDBConverter::toAttributeValue).toList()).build(); if (value instanceof Map mapValue) { HashMap values = new HashMap<>(); mapValue.forEach((k, v) -> values.put(String.valueOf(k), toAttributeValue(v))); diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java index 12ad2312e..f93f6a072 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java @@ -27,7 +27,7 @@ import java.util.Map; import java.util.function.Supplier; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.toAttributeValue; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toAttributeValue; abstract class DynamoDBQueryBuilder implements Supplier { From f985eda124035a96a32cff1eeedc8915d5d6594f Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:22:22 -0300 Subject: [PATCH 49/70] chore: add beans.xml Signed-off-by: Maximillian Arruda --- .../src/main/resources/META-INF/beans.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 jnosql-dynamodb/src/main/resources/META-INF/beans.xml diff --git a/jnosql-dynamodb/src/main/resources/META-INF/beans.xml b/jnosql-dynamodb/src/main/resources/META-INF/beans.xml new file mode 100644 index 000000000..6130628ce --- /dev/null +++ b/jnosql-dynamodb/src/main/resources/META-INF/beans.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file From b51dfef7a1b2fe873015403dc633b5b1ad80896e Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:24:17 -0300 Subject: [PATCH 50/70] chore: add DynamoDBRepository Signed-off-by: Maximillian Arruda --- .../dynamodb/mapping/DynamoDBRepository.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepository.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepository.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepository.java new file mode 100644 index 000000000..122df784a --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepository.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.data.repository.BasicRepository; + +public interface DynamoDBRepository extends BasicRepository { +} From 7f069b6d9a7400d41d9f38b34b5331f72a3a8d4f Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:24:55 -0300 Subject: [PATCH 51/70] chore: add DynamoDBTemplate Signed-off-by: Maximillian Arruda --- .../dynamodb/mapping/DynamoDBTemplate.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java new file mode 100644 index 000000000..44d16310a --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import org.eclipse.jnosql.communication.document.DocumentEntity; +import org.eclipse.jnosql.mapping.document.JNoSQLDocumentTemplate; + +import java.util.stream.Stream; + +/** + * The {@code DynamoDBTemplate} is an interface that extends {@link JNoSQLDocumentTemplate} and + * provides methods for executing Dynamo DB queries using the PartiQL Language. + *

DynamoDB supports a limited subset of + * PartiQL. + *

+ *

+ * It allows you to interact with the DynamoDB database using PartiQL queries to retrieve and + * process data in a more flexible and customizable way. + *

+ * + * @see JNoSQLDocumentTemplate + */ +public interface DynamoDBTemplate extends JNoSQLDocumentTemplate { + + /** + * Executes a DynamoDB query using + * PartiQL. + * + * @param query the PartiQL query + * @return a {@link Stream} of results representing the query result + * @throws NullPointerException when the query is null + */ + Stream partiQL(String query); + + /** + * Executes a DynamoDB query using + * PartiQL with parameters. + *

Example query: {@code SELECT * FROM users WHERE status = ?}

+ * + * @param query the PartiQL query + * @return a {@link Stream} of {@link DocumentEntity} representing the query result + * @throws NullPointerException when the query is null + */ + Stream partiQL(String query, Object... params); +} From 5b09e1666a902318a5ce6fb43d46795227038c3a Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:26:20 -0300 Subject: [PATCH 52/70] chore: add PartiQL annotation Signed-off-by: Maximillian Arruda --- .../databases/dynamodb/mapping/PartiQL.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/PartiQL.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/PartiQL.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/PartiQL.java new file mode 100644 index 000000000..128919a01 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/PartiQL.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@code PartiQL} annotation is a custom annotation used to associate PartiQL query strings + * with methods or elements in your code. + * + *

DynamoDB supports a limited subset of + * PartiQL. + *

+ *

+ *

+ * When applied to a method, field, or other element, this annotation provides a convenient + * way to specify an PartiQL query that should be associated with that element. + *

+ *

+ * For example, you can use this annotation to specify a PartiQL query for a method as follows: + *

+ *
+ * {@code
+ * @PartiQL("SELECT * FROM users WHERE status = 'active'")
+ * public void getUserData() {
+ *     // Method implementation
+ * }
+ * }
+ * 
+ *

+ * In the context of an {@code DynamoDBRepository}, you can use the {@code @PartiQL} annotation + * to define custom PartiQL queries for repository methods. Here's an example: + *

+ *
+ * {@code
+ * public interface UserRepository extends DynamoDBRepository {
+ *     // Find all active users using a custom SQL query
+ *     @PartiQL("SELECT * FROM users WHERE status = 'active'")
+ *     List findActiveUsers();
+ * }
+ * }
+ * 
+ *

+ * In this example, the {@code @PartiQL} annotation is used to define a custom PartiQL query for the + * {@code findActiveUsers} method in an {@code DynamoDBRepository} interface, allowing you + * to execute the specified PartiQL query when calling the repository method. + *

+ */ + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface PartiQL { + + /** + * The PartiQL query to associated with the annotated method or element. + * + *

DynamoDB supports a limited subset of + * PartiQL. + *

+ * + * @return the PartiQL query + */ + String value(); +} From dc0fb1186e5de3060d328afe11b1d5360d5f95ac Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:26:48 -0300 Subject: [PATCH 53/70] chore: add package-info Signed-off-by: Maximillian Arruda --- .../jnosql/databases/dynamodb/package-info.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/package-info.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/package-info.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/package-info.java new file mode 100644 index 000000000..62858f422 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb; \ No newline at end of file From 48f0db3461135108e3c76327a3e770434a86cfda Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:32:11 -0300 Subject: [PATCH 54/70] test: add classes for mapping test purposes Signed-off-by: Maximillian Arruda --- .../databases/dynamodb/mapping/Person.java | 82 +++++++++++++++++++ .../mapping/PersonNoSQLRepository.java | 22 +++++ 2 files changed, 104 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/Person.java create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/PersonNoSQLRepository.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/Person.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/Person.java new file mode 100644 index 000000000..40e3705a5 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/Person.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.nosql.Entity; +import jakarta.nosql.Id; +import jakarta.nosql.Column; + +import java.util.Objects; + +@Entity +public class Person { + + @Id + private String name; + + @Column + private Integer age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public Person(String name, Integer age) { + this.name = name; + this.age = age; + } + + public Person() { + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Person person = (Person) o; + return Objects.equals(name, person.name) && + Objects.equals(age, person.age); + } + + @Override + public int hashCode() { + return Objects.hash(name, age); + } + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", age=" + age + + '}'; + } + + public static Person of(String name, Integer age) { + return new Person(name, age); + } +} \ No newline at end of file diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/PersonNoSQLRepository.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/PersonNoSQLRepository.java new file mode 100644 index 000000000..d05fd8084 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/PersonNoSQLRepository.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.data.repository.Repository; + +@Repository +public interface PersonNoSQLRepository extends DynamoDBRepository { +} From c676c273976f7d2612846847991d46ff8082442c Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:32:46 -0300 Subject: [PATCH 55/70] test: add mock producer for mapping test purposes Signed-off-by: Maximillian Arruda --- .../dynamodb/mapping/MockProducer.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/MockProducer.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/MockProducer.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/MockProducer.java new file mode 100644 index 000000000..6a9c909da --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/MockProducer.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.annotation.Priority; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Produces; +import jakarta.interceptor.Interceptor; +import org.eclipse.jnosql.communication.document.Document; +import org.eclipse.jnosql.communication.document.DocumentEntity; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentManager; +import org.mockito.Mockito; + +import java.util.function.Supplier; + +@Alternative +@Priority(Interceptor.Priority.APPLICATION) +public class MockProducer implements Supplier { + + @Produces + @Override + public DynamoDBDocumentManager get() { + DynamoDBDocumentManager manager = Mockito.mock(DynamoDBDocumentManager.class); + DocumentEntity entity = DocumentEntity.of("Person"); + entity.add(Document.of("name", "Ada")); + Mockito.when(manager.insert(Mockito.any(DocumentEntity.class))).thenReturn(entity); + return manager; + } + +} \ No newline at end of file From 53cc68c7f2632f84ccbccaf05958eed7fc5c87cb Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:33:56 -0300 Subject: [PATCH 56/70] chore: add DefaultDynamoDBTemplate implementation Signed-off-by: Maximillian Arruda --- .../mapping/DefaultDynamoDBTemplate.java | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplate.java diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplate.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplate.java new file mode 100644 index 000000000..44ace7cd3 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplate.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.Typed; +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.document.DocumentManager; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.document.AbstractDocumentTemplate; +import org.eclipse.jnosql.mapping.document.DocumentEntityConverter; +import org.eclipse.jnosql.mapping.document.DocumentEventPersistManager; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + +import java.util.Objects; +import java.util.stream.Stream; + +@Typed(DynamoDBTemplate.class) +@ApplicationScoped +class DefaultDynamoDBTemplate extends AbstractDocumentTemplate implements DynamoDBTemplate { + + private Instance manager; + + private DocumentEntityConverter converter; + + private DocumentEventPersistManager persistManager; + + private EntitiesMetadata entitiesMetadata; + + private Converters converters; + + @Inject + DefaultDynamoDBTemplate(Instance manager, + DocumentEntityConverter converter, + DocumentEventPersistManager persistManager, + EntitiesMetadata entitiesMetadata, + Converters converters) { + this.manager = manager; + this.converter = converter; + this.persistManager = persistManager; + this.entitiesMetadata = entitiesMetadata; + this.converters = converters; + } + + /** + * Required by CDI/Reflection/Test purposes + * Don't use it + */ + DefaultDynamoDBTemplate() { + } + + @Override + protected DocumentEntityConverter getConverter() { + return converter; + } + + @Override + protected DocumentManager getManager() { + return manager.get(); + } + + @Override + protected DocumentEventPersistManager getEventManager() { + return persistManager; + } + + @Override + protected EntitiesMetadata getEntities() { + return entitiesMetadata; + } + + @Override + protected Converters getConverters() { + return converters; + } + + @Override + @SuppressWarnings("unchecked") + public Stream partiQL(String query) { + Objects.requireNonNull(query, "query is required"); + return manager.get().partiQL(query).map(converter::toEntity).map(d -> (T) d); + } + + @Override + @SuppressWarnings("unchecked") + public Stream partiQL(String query, Object... params) { + Objects.requireNonNull(query, "query is required"); + Objects.requireNonNull(params, "params is required"); + return manager.get().partiQL(query, params).map(converter::toEntity).map(d -> (T) d); + } +} From c1f7298dadb7d29d925b0833b18ec6ff3be96e4a Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:35:58 -0300 Subject: [PATCH 57/70] chore: add cdi extension Signed-off-by: Maximillian Arruda --- .../mapping/DocumentManagerSupplier.java | 67 ++++++++ .../dynamodb/mapping/DynamoDBExtension.java | 44 ++++++ .../mapping/DynamoDBRepositoryBean.java | 82 ++++++++++ .../mapping/DynamoDBRepositoryProxy.java | 149 ++++++++++++++++++ .../jakarta.enterprise.inject.spi.Extension | 15 ++ 5 files changed, 357 insertions(+) create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DocumentManagerSupplier.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtension.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryBean.java create mode 100644 jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxy.java create mode 100644 jnosql-dynamodb/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DocumentManagerSupplier.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DocumentManagerSupplier.java new file mode 100644 index 000000000..ffee5d86a --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DocumentManagerSupplier.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.data.exceptions.MappingException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Disposes; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.Typed; +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentConfiguration; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentManager; +import org.eclipse.jnosql.mapping.core.config.MicroProfileSettings; + +import java.util.Optional; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.DOCUMENT_DATABASE; + +@ApplicationScoped +public class DocumentManagerSupplier implements Supplier { + + private static final Logger LOGGER = Logger.getLogger(DocumentManagerSupplier.class.getName()); + + @Override + @Produces + @Typed(DynamoDBDocumentManager.class) + @ApplicationScoped + public DynamoDBDocumentManager get() { + Settings settings = MicroProfileSettings.INSTANCE; + var configuration = new DynamoDBDocumentConfiguration(); + var factory = configuration.apply(settings); + Optional database = settings.get(DOCUMENT_DATABASE, String.class); + String db = database.orElseThrow(() -> new MappingException("Please, inform the database filling up the property " + + DOCUMENT_DATABASE.get())); + DynamoDBDocumentManager manager = (DynamoDBDocumentManager) factory.apply(db); + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.log(Level.FINEST, """ + Starting a DynamoDBDocumentManager instance using Eclipse MicroProfile Config,\ + database name: %s + """.formatted(db)); + } + return manager; + } + + public void close(@Disposes DynamoDBDocumentManager manager) { + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.log(Level.FINEST, "Closing OracleDocumentManager resource, database name: %s".formatted(manager.name())); + } + manager.close(); + } +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtension.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtension.java new file mode 100644 index 000000000..37fe6a6c7 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtension.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.AfterBeanDiscovery; +import jakarta.enterprise.inject.spi.Extension; +import org.eclipse.jnosql.mapping.metadata.ClassScanner; + +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class DynamoDBExtension implements Extension { + + private static final Logger LOGGER = Logger.getLogger(DynamoDBExtension.class.getName()); + + void onAfterBeanDiscovery(@Observes final AfterBeanDiscovery afterBeanDiscovery) { + + ClassScanner scanner = ClassScanner.load(); + Set> crudTypes = scanner.repositories(DynamoDBRepository.class); + + if (LOGGER.isLoggable(Level.INFO)) + LOGGER.info("Starting the onAfterBeanDiscovery with elements number: %s".formatted(crudTypes.size())); + + crudTypes.forEach(type -> afterBeanDiscovery.addBean(new DynamoDBRepositoryBean<>(type))); + + if (LOGGER.isLoggable(Level.INFO)) + LOGGER.info("Finished the onAfterBeanDiscovery"); + } +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryBean.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryBean.java new file mode 100644 index 000000000..be50d23de --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryBean.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.enterprise.context.spi.CreationalContext; +import jakarta.enterprise.inject.Default; +import jakarta.enterprise.util.AnnotationLiteral; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.AbstractBean; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Set; + +class DynamoDBRepositoryBean extends AbstractBean> { + + private final Class type; + + private final Set types; + + private final Set qualifiers = Collections.singleton(new AnnotationLiteral() { + }); + + @SuppressWarnings({"rawtypes", "unchecked"}) + DynamoDBRepositoryBean(Class type) { + this.type = type; + this.types = Collections.singleton(type); + } + + @Override + public Class getBeanClass() { + return type; + } + + @Override + @SuppressWarnings("unchecked") + public DynamoDBRepository create(CreationalContext> creationalContext) { + + DynamoDBTemplate template = getInstance(DynamoDBTemplate.class); + Converters converters = getInstance(Converters.class); + EntitiesMetadata entitiesMetadata = getInstance(EntitiesMetadata.class); + + DynamoDBRepositoryProxy handler = new DynamoDBRepositoryProxy<>( + template, type, converters, entitiesMetadata); + + return (DynamoDBRepository) Proxy.newProxyInstance(type.getClassLoader(), + new Class[]{type}, + handler); + } + + @Override + public Set getTypes() { + return types; + } + + @Override + public Set getQualifiers() { + return qualifiers; + } + + @Override + public String getId() { + return type.getName() + '@' + "dynamodb"; + } + +} diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxy.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxy.java new file mode 100644 index 000000000..b65b80bf7 --- /dev/null +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxy.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.data.repository.Param; +import jakarta.inject.Inject; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.query.AbstractRepository; +import org.eclipse.jnosql.mapping.core.repository.DynamicReturn; +import org.eclipse.jnosql.mapping.document.JNoSQLDocumentTemplate; +import org.eclipse.jnosql.mapping.document.query.AbstractDocumentRepositoryProxy; +import org.eclipse.jnosql.mapping.document.query.DocumentRepositoryProxy; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.metadata.EntityMetadata; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.eclipse.jnosql.mapping.core.repository.DynamicReturn.toSingleResult; + +class DynamoDBRepositoryProxy extends AbstractDocumentRepositoryProxy { + + private final DynamoDBTemplate template; + + private final Class type; + + private final Converters converters; + + private final Class typeClass; + + private final EntityMetadata entityMetadata; + + private final AbstractRepository repository; + + @Inject + @SuppressWarnings("unchecked") + DynamoDBRepositoryProxy(DynamoDBTemplate template, + Class type, + Converters converters, + EntitiesMetadata entitiesMetadata) { + + this.template = template; + this.type = type; + this.typeClass = (Class) ((ParameterizedType) type.getGenericInterfaces()[0]).getActualTypeArguments()[0]; + this.converters = converters; + this.entityMetadata = entitiesMetadata.get(typeClass); + this.repository = DocumentRepositoryProxy.DocumentRepository.of(template, entityMetadata); + } + + /** + * Required by CDI/Reflection/Test purposes + * Don't use it + */ + DynamoDBRepositoryProxy() { + this.template = null; + this.type = null; + this.typeClass = null; + this.converters = null; + this.entityMetadata = null; + this.repository = null; + } + + @Override + @SuppressWarnings("unchecked") + public Object invoke(Object instance, Method method, Object[] args) throws Throwable { + PartiQL sql = method.getAnnotation(PartiQL.class); + if (Objects.nonNull(sql)) { + Stream result; + List params = getParams(args, method); + if (params.isEmpty()) { + result = template.partiQL(sql.value()); + } else { + result = template.partiQL(sql.value(), params.toArray()); + } + return DynamicReturn.builder() + .withClassSource(typeClass) + .withMethodSource(method) + .withResult(() -> result) + .withSingleResult(toSingleResult(method).apply(() -> result)) + .build().execute(); + } + return super.invoke(instance, method, args); + } + + private List getParams(Object[] args, Method method) { + + List params = new ArrayList<>(); + Annotation[][] annotations = method.getParameterAnnotations(); + + for (int index = 0; index < annotations.length; index++) { + + final Object arg = args[index]; + + Optional param = Stream.of(annotations[index]) + .filter(Param.class::isInstance) + .map(Param.class::cast) + .findFirst(); + param.ifPresent(p -> params.add(arg)); + + } + + return params; + } + + @Override + protected Converters converters() { + return converters; + } + + @Override + @SuppressWarnings({"rawtypes","unchecked"}) + protected AbstractRepository repository() { + return repository; + } + + @Override + protected Class repositoryType() { + return type; + } + + @Override + protected EntityMetadata entityMetadata() { + return entityMetadata; + } + + @Override + protected JNoSQLDocumentTemplate template() { + return template; + } +} diff --git a/jnosql-dynamodb/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/jnosql-dynamodb/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension new file mode 100644 index 000000000..cd65d5e74 --- /dev/null +++ b/jnosql-dynamodb/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension @@ -0,0 +1,15 @@ +# +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# and Apache License v2.0 which accompanies this distribution. +# The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html +# and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. +# +# You may elect to redistribute this code under either of these licenses. +# +# Contributors: +# +# Maximillian Arruda +# +org.eclipse.jnosql.databases.dynamodb.mapping.DynamoDBExtension \ No newline at end of file From 4ba35501de159cb5c36f32bf87ed2f9b3d4508d3 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:37:08 -0300 Subject: [PATCH 58/70] test: fixed imports and references Signed-off-by: Maximillian Arruda --- .../DefaultDynamoDBDocumentManagerTest.java | 8 ++++---- .../communication/DocumentEntityGenerator.java | 2 +- ...nverterTest.java => DynamoDBConverterTest.java} | 10 +++++----- .../dynamodb/communication/DynamoDBTestUtils.java | 14 ++++++++++---- 4 files changed, 20 insertions(+), 14 deletions(-) rename jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/{DocumentEntityConverterTest.java => DynamoDBConverterTest.java} (87%) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java index 48aa3565e..cd6cfcb4c 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java @@ -48,7 +48,7 @@ import static org.eclipse.jnosql.communication.document.DocumentQuery.select; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityConverter.ID; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.ID; import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityGenerator.createRandomEntity; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils.CONFIG; @@ -143,7 +143,7 @@ void shouldInsert() { assertSoftly(softly -> { DocumentEntity entity = createRandomEntity(); var _entityType = entity.name(); - var id = entity.find(DocumentEntityConverter.ID, String.class).orElseThrow(); + var id = entity.find(DynamoDBConverter.ID, String.class).orElseThrow(); var persistedEntity = documentManager.insert(entity); softly.assertThat(persistedEntity) @@ -204,7 +204,7 @@ void shouldUpdate() { documentManager.insert(List.of(entity1, entity2, entity3)); final BiConsumer assertions = (softly, updatedEntity) -> { - Map item = getItem(updatedEntity.name(), updatedEntity.find(DocumentEntityConverter.ID, String.class).orElseThrow()); + Map item = getItem(updatedEntity.name(), updatedEntity.find(DynamoDBConverter.ID, String.class).orElseThrow()); softly.assertThat(item.get("name")) .as("the name attribute should exists in the returned item from dynamodb") .isNotNull(); @@ -245,7 +245,7 @@ private Map getItem(String _entityType, String id) { .tableName(_entityType) .key(Map.of( entityNameResolver.apply(_entityType), AttributeValue.builder().s(_entityType).build(), - DocumentEntityConverter.ID, AttributeValue.builder().s(id).build() + DynamoDBConverter.ID, AttributeValue.builder().s(id).build() )) .build()) .item(); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java index f99a1d38b..ae364826f 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java @@ -50,7 +50,7 @@ static DocumentEntity createRandomEntityWithSubDocuments(int levels) { @NotNull private static DocumentEntity createRandomEntityWithSubDocuments(String collectionName, int levels) { Map map = new HashMap<>(); - map.put(DocumentEntityConverter.ID, UUID.randomUUID().toString()); + map.put(DynamoDBConverter.ID, UUID.randomUUID().toString()); map.put("name", faker.name().firstName()); map.put("hoje", LocalDate.now()); map.put("agora", LocalDateTime.now()); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java similarity index 87% rename from jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java rename to jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java index fd6aaa3be..c734c4d99 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityConverterTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java @@ -34,7 +34,7 @@ import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) -class DocumentEntityConverterTest { +class DynamoDBConverterTest { static final Faker faker = new Faker(); @@ -57,13 +57,13 @@ void shouldConvertToItemRequest() { Document.of("phones", List.of(faker.name().firstName(), faker.name().firstName(), faker.name().firstName())) )); - var item = DocumentEntityConverter.toItem(entityNameResolver, entity); + var item = DynamoDBConverter.toItem(entityNameResolver, entity); - var entityFromItem = DocumentEntityConverter.toDocumentEntity(entityNameResolver, item); + var entityFromItem = DynamoDBConverter.toDocumentEntity(entityNameResolver, item); - var expected = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entity)))).readObject(); + var expected = Json.createReader(new StringReader(JSONB.toJson(DynamoDBConverter.getMap(entityNameResolver, entity)))).readObject(); - var actual = Json.createReader(new StringReader(JSONB.toJson(DocumentEntityConverter.getMap(entityNameResolver, entityFromItem)))).readObject(); + var actual = Json.createReader(new StringReader(JSONB.toJson(DynamoDBConverter.getMap(entityNameResolver, entityFromItem)))).readObject(); softly.assertThat(actual).as("cannot convert a simple DocumentEntity") .isEqualTo(expected); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index dd987923b..f0f7b080d 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -26,7 +26,7 @@ import java.util.function.UnaryOperator; -enum DynamoDBTestUtils { +public enum DynamoDBTestUtils { CONFIG; @@ -63,7 +63,7 @@ DocumentManager getDocumentManager(Settings settings) { return getDocumentManagerFactory(settings).apply(database); } - Settings getSettings() { + public Settings getSettings() { dynamodb.start(); String dynamoDBHost = getDynamoDBHost(dynamodb.getHost(), dynamodb.getFirstMappedPort()); return getSettings(dynamoDBHost); @@ -75,7 +75,7 @@ Settings getSettings(String dynamoDBHost) { .build(); } - Settings customSetting(SettingsBuilder builder) { + public Settings customSetting(SettingsBuilder builder) { var defaultSetting = getSettings(); var customSetting = builder.build(); return Settings.builder() @@ -85,7 +85,7 @@ Settings customSetting(SettingsBuilder builder) { } @NotNull - private static SettingsBuilder getSettingsBuilder(UnaryOperator builder) { + public static SettingsBuilder getSettingsBuilder(UnaryOperator builder) { return builder.apply(Settings.builder()) .put(MappingConfigurations.DOCUMENT_DATABASE, "test") .put(DynamoDBConfigurations.AWS_ACCESSKEY, System.getProperty("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE")) @@ -95,6 +95,12 @@ private static SettingsBuilder getSettingsBuilder(UnaryOperator .put(DynamoDBConfigurations.ENTITY_PARTITION_KEY, "entityType"); } + public void setupSystemProperties(SettingsBuilder builder) { + Settings settings = customSetting(builder); + System.getProperties().putAll(settings.toMap()); + System.out.println(System.getProperties()); + } + @NotNull String getDynamoDBHost(String host, int port) { return "http://" + host + ":" + port; From 16509bdba62f5069f503853379c9b10eea4cd763 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:37:39 -0300 Subject: [PATCH 59/70] test: add tests for cdi extension Signed-off-by: Maximillian Arruda --- .../mapping/DynamoDBExtensionTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtensionTest.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtensionTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtensionTest.java new file mode 100644 index 000000000..4c4795076 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtensionTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.mapping.Convert; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; +import org.eclipse.jnosql.mapping.keyvalue.spi.KeyValueExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@EnableAutoWeld +@AddPackages(value = {Convert.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, + DocumentExtension.class, DynamoDBExtension.class}) +@ExtendWith(MockitoExtension.class) +public class DynamoDBExtensionTest { + + @Inject + private PersonNoSQLRepository repository; + + @Test + public void shouldSave() { + Assertions.assertNotNull(repository); + } +} From 0328ef59687b724624145505341acbd72373ed91 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:38:19 -0300 Subject: [PATCH 60/70] test: add tests for DefaultDynamoDBTemplate implementation Signed-off-by: Maximillian Arruda --- .../mapping/DefaultDynamoDBTemplateTest.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplateTest.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplateTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplateTest.java new file mode 100644 index 000000000..8ee5ba532 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplateTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; +import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentManager; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.document.DocumentEntityConverter; +import org.eclipse.jnosql.mapping.document.DocumentEventPersistManager; +import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.mockito.Mockito.when; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, DocumentEntityConverter.class, PartiQL.class}) +@AddPackages(MockProducer.class) +@AddExtensions({EntityMetadataExtension.class, DocumentExtension.class, DynamoDBExtension.class}) +@ExtendWith(MockitoExtension.class) +@AddPackages(Reflections.class) +class DefaultDynamoDBTemplateTest { + + @Inject + private DocumentEntityConverter converter; + + @Inject + private DocumentEventPersistManager persistManager; + + @Inject + private EntitiesMetadata entities; + + @Inject + private Converters converters; + + private DynamoDBDocumentManager manager; + + private DynamoDBTemplate template; + + @BeforeEach + void setup() { + manager = Mockito.mock(DynamoDBDocumentManager.class); + Instance instance = Mockito.mock(Instance.class); + when(instance.get()).thenReturn(manager); + template = new DefaultDynamoDBTemplate(instance, converter, persistManager, entities, converters); + } + + @Test + public void shouldFindSQL() { + template.partiQL("select from database"); + Mockito.verify(manager).partiQL("select from database"); + } + + @Test + public void shouldFindSQLWithTypeAndParameters() { + template.partiQL("select from database where content.name = ?", List.of("Ada"), String.class); + Mockito.verify(manager).partiQL("select from database where content.name = ?", List.of("Ada"), String.class); + } + + @Test + public void shouldDeleteAll(){ + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(DocumentDeleteQuery.class); + template.deleteAll(Person.class); + Mockito.verify(manager).delete(argumentCaptor.capture()); + DocumentDeleteQuery query = argumentCaptor.getValue(); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(query.name()).isEqualTo("Person"); + soft.assertThat(query.documents()).isEmpty(); + soft.assertThat(query.condition()).isEmpty(); + }); + + } + + +} From f564e19e16cc12e114f01673d57f227c78e0ba30 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:38:52 -0300 Subject: [PATCH 61/70] test: add tests for DynamoDBRepositoryProxy implementation Signed-off-by: Maximillian Arruda --- .../mapping/DynamoDBRepositoryProxyTest.java | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxyTest.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxyTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxyTest.java new file mode 100644 index 000000000..b1b5d5933 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxyTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.mapping; + +import jakarta.data.repository.Param; +import jakarta.inject.Inject; +import org.assertj.core.api.Assertions; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.document.DocumentEntityConverter; +import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; +import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.lang.reflect.Proxy; +import java.time.Duration; +import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@EnableAutoWeld +@AddPackages(value = {Converters.class, DocumentEntityConverter.class, PartiQL.class}) +@AddPackages(MockProducer.class) +@AddPackages(Reflections.class) +@AddExtensions({EntityMetadataExtension.class, DocumentExtension.class, DynamoDBExtension.class}) +class DynamoDBRepositoryProxyTest { + + private DynamoDBTemplate template; + + @Inject + private EntitiesMetadata entitiesMetadata; + + @Inject + private Converters converters; + + private PersonNoSQLRepository personRepository; + + @SuppressWarnings("rawtypes") + @BeforeEach + void setUp() { + this.template = Mockito.mock(DynamoDBTemplate.class); + + DynamoDBRepositoryProxy handler = new DynamoDBRepositoryProxy<>(template, + PersonNoSQLRepository.class, converters, entitiesMetadata); + + when(template.insert(any(Person.class))).thenReturn(new Person()); + when(template.insert(any(Person.class), any(Duration.class))).thenReturn(new Person()); + when(template.update(any(Person.class))).thenReturn(new Person()); + + this.personRepository = (PersonNoSQLRepository) Proxy.newProxyInstance(PersonNoSQLRepository.class.getClassLoader(), + new Class[]{PersonNoSQLRepository.class}, + handler); + } + + @Test + public void shouldFindAll() { + personRepository.findAllQuery(); + verify(template).partiQL("select * from Person"); + } + + @Test + public void shouldFindByNameSQL() { + ArgumentCaptor captor = ArgumentCaptor.forClass(Object[].class); + personRepository.findByName("Ada"); + verify(template).partiQL(eq("select * from Person where name= ?"), captor.capture()); + + Object[] value = captor.getValue(); + Assertions.assertThat(value).hasSize(1).contains("Ada"); + } + + @Test + public void shouldSaveUsingInsert() { + Person person = Person.of("Ada", 10); + personRepository.save(person); + verify(template).insert(eq(person)); + } + + + @Test + public void shouldSaveUsingUpdate() { + Person person = Person.of("Ada-2", 10); + when(template.find(Person.class, "Ada-2")).thenReturn(Optional.of(person)); + personRepository.save(person); + verify(template).update(eq(person)); + } + + @Test + public void shouldDelete(){ + personRepository.deleteById("id"); + verify(template).delete(Person.class, "id"); + } + + + @Test + public void shouldDeleteEntity(){ + Person person = Person.of("Ada", 10); + personRepository.delete(person); + verify(template).delete(Person.class, person.getName()); + } + + @Test + public void shouldDeleteAll() { + ArgumentCaptor> queryCaptor = ArgumentCaptor.forClass(Class.class); + + personRepository.deleteAll(); + verify(template).deleteAll(queryCaptor.capture()); + + Class query = queryCaptor.getValue(); + Assertions.assertThat(query).isEqualTo(Person.class); + } + + interface PersonNoSQLRepository extends DynamoDBRepository { + + @PartiQL("select * from Person") + List findAllQuery(); + + @PartiQL("select * from Person where name= ?") + List findByName(@Param("") String name); + } + +} From 8244687fd96ebc0bd0742dad9d5085a8ae84c9be Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:39:23 -0300 Subject: [PATCH 62/70] test: add integration tests for dynamodb document implementation Signed-off-by: Maximillian Arruda --- .../databases/dynamodb/integration/Book.java | 80 ++++++++++ .../DynamoDBTemplateIntegrationTest.java | 140 ++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/Book.java create mode 100644 jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/DynamoDBTemplateIntegrationTest.java diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/Book.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/Book.java new file mode 100644 index 000000000..b6160ec61 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/Book.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.integration; + +import jakarta.nosql.Column; +import jakarta.nosql.Entity; +import jakarta.nosql.Id; + +import java.util.Objects; + +@Entity +public class Book { + + @Id("id") + private String id; + + @Column("title") + private String title; + + @Column("edition") + private int edition; + + public Book(String id, String title, int edition) { + this.id = id; + this.title = title; + this.edition = edition; + } + + Book() { + } + + public String id() { + return id; + } + + public String title() { + return title; + } + + public int edition() { + return edition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Book book = (Book) o; + return edition == book.edition + && Objects.equals(id, book.id) + && Objects.equals(title, book.title); + } + + @Override + public int hashCode() { + return Objects.hash(id, title, edition); + } + + @Override + public String toString() { + return "Book{" + + "id='" + id + '\'' + + ", title='" + title + '\'' + + ", edition=" + edition + + '}'; + } +} \ No newline at end of file diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/DynamoDBTemplateIntegrationTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/DynamoDBTemplateIntegrationTest.java new file mode 100644 index 000000000..bcb2eb116 --- /dev/null +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/DynamoDBTemplateIntegrationTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php. + * + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * + * Maximillian Arruda + */ + +package org.eclipse.jnosql.databases.dynamodb.integration; + +import jakarta.inject.Inject; +import org.eclipse.jnosql.communication.Settings; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConfigurations; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils; +import org.eclipse.jnosql.databases.dynamodb.mapping.DynamoDBExtension; +import org.eclipse.jnosql.databases.dynamodb.mapping.DynamoDBTemplate; +import org.eclipse.jnosql.mapping.Convert; +import org.eclipse.jnosql.mapping.core.Converters; +import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; +import org.eclipse.jnosql.mapping.document.DocumentEntityConverter; +import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; +import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.jboss.weld.junit5.auto.AddExtensions; +import org.jboss.weld.junit5.auto.AddPackages; +import org.jboss.weld.junit5.auto.EnableAutoWeld; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; + +import java.util.Optional; + +import static java.util.UUID.randomUUID; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; +import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; + +@EnableAutoWeld +@AddPackages(value = {Convert.class, DocumentEntityConverter.class}) +@AddPackages(Book.class) +@AddPackages(DynamoDBTemplate.class) +@AddExtensions({EntityMetadataExtension.class, DocumentExtension.class, DynamoDBExtension.class}) +@AddPackages(Reflections.class) +@AddPackages(Converters.class) +@EnabledIfSystemProperty(named = NAMED, matches = MATCHES) +class DynamoDBTemplateIntegrationTest { + + static { + DynamoDBTestUtils.CONFIG.setupSystemProperties(Settings.builder() + .put(DynamoDBConfigurations.CREATE_TABLES, "true")); + } + + @Inject + private DynamoDBTemplate template; + + @Test + void shouldInsert() { + Book book = new Book(randomUUID().toString(), "Effective Java", 1); + template.insert(book); + Optional optional = template.find(Book.class, book.id()); + assertThat(optional).isNotNull().isNotEmpty() + .get().isEqualTo(book); + } + + @Test + void shouldUpdate() { + Book book = new Book(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(book)) + .isNotNull() + .isEqualTo(book); + + Book updated = new Book(book.id(), book.title() + " updated", 2); + + assertThat(template.update(updated)) + .isNotNull() + .isNotEqualTo(book); + + assertThat(template.find(Book.class, book.id())) + .isNotNull().get().isEqualTo(updated); + + } + + @Test + void shouldFindById() { + Book book = new Book(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(book)) + .isNotNull() + .isEqualTo(book); + + assertThat(template.find(Book.class, book.id())) + .isNotNull().get().isEqualTo(book); + } + + @Test + void shouldDelete() { + Book book = new Book(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(book)) + .isNotNull() + .isEqualTo(book); + + template.delete(Book.class, book.id()); + assertThat(template.find(Book.class, book.id())) + .isNotNull().isEmpty(); + } + + @Test + void shouldDeleteAll() { + for (int index = 0; index < 20; index++) { + Book book = new Book(randomUUID().toString(), "Effective Java", 1); + assertThat(template.insert(book)) + .isNotNull() + .isEqualTo(book); + } + + template.delete(Book.class).execute(); + assertThat(template.select(Book.class).result()).isEmpty(); + } + + @Test + void shouldUpdateNullValues() { + var book = new Book(randomUUID().toString(), "Effective Java", 1); + template.insert(book); + template.update(new Book(book.id(), null, 2)); + Optional optional = template.select(Book.class).where("id") + .eq(book.id()).singleResult(); + assertSoftly(softly -> { + softly.assertThat(optional).isPresent(); + softly.assertThat(optional).get().extracting(Book::title).isNull(); + softly.assertThat(optional).get().extracting(Book::edition).isEqualTo(2); + }); + } + + +} From 675b6fbe50b481fee0a8f15cb37de406b6207702 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 02:40:59 -0300 Subject: [PATCH 63/70] chore: removed unused imports Signed-off-by: Maximillian Arruda --- .../databases/dynamodb/communication/DynamoDBConverter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java index 01ed968db..09876c31c 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java @@ -35,7 +35,6 @@ import java.util.stream.StreamSupport; import static java.util.Collections.singletonMap; -import static java.util.stream.Collectors.toList; class DynamoDBConverter { From 10812df664f38333b7cd3fcc05ff5629d407da49 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 03:46:41 -0300 Subject: [PATCH 64/70] chore: fix the returned type Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DynamoDBDocumentManagerFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java index c101a2267..3edc940a1 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java @@ -33,7 +33,7 @@ public DynamoDBDocumentManagerFactory(DynamoDbClient dynamoDB, Settings settings } @Override - public DocumentManager apply(String database) { + public DynamoDBDocumentManager apply(String database) { return new DefaultDynamoDBDocumentManager(database, dynamoDB, settings); } From c6a03e04c8a734efb70804a7b5e4630216ce0c58 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 06:53:27 -0300 Subject: [PATCH 65/70] changelog: added document api support for dynamodb Signed-off-by: Maximillian Arruda --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 1ad7e98a2..eade22bfd 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -31,6 +31,7 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version === Added - Include support to Oracle NoSQL database +- Include support to Document API for DynamoDB database == [1.0.4] - 2023-12-19 From 425609d262f08eb10ec749b43fe3f5a843a10219 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 03:47:49 -0300 Subject: [PATCH 66/70] docs: added document api support for dynamodb Signed-off-by: Maximillian Arruda --- README.adoc | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/README.adoc b/README.adoc index 0b23fbf0a..ee27c57f7 100644 --- a/README.adoc +++ b/README.adoc @@ -390,7 +390,6 @@ jnosql.couchbase.user=root jnosql.couchbase.password=123456 ---- - The config settings are the default behavior; nevertheless, there is an option to do it programmatically. Create a class that implements the `Supplier` and then defines it as an `@Alternative` and the `Priority`. [source,java] @@ -519,9 +518,9 @@ jnosql.couchdb.password=password image::https://user-images.githubusercontent.com/6509926/70553550-f033b980-1b40-11ea-9192-759b3b1053b3.png[Redis Project,align="center" width=50%,height=50%] -https://aws.amazon.com/dynamodb/[Amazon DynamoDB] is a fully managed, serverless, key-value NoSQL database designed to run high-performance applications at any scale. DynamoDB offers built-in security, continuous backups, automated multi-Region replication, in-memory caching, and data import and export tools. +https://aws.amazon.com/dynamodb/[Amazon DynamoDB] is a fully managed, serverless, key-value and document NoSQL database designed to run high-performance applications at any scale. DynamoDB offers built-in security, continuous backups, automated multi-Region replication, in-memory caching, and data import and export tools. -This driver provides support for the *Key-Value* NoSQL API. +This driver has support for two NoSQL API types: *Key-Value* and *Document*. === How To Install @@ -539,6 +538,7 @@ You can use either the Maven or Gradle dependencies: === Configuration This API provides the ```DynamoDBConfigurations``` class to programmatically establish the credentials. + Please note that you can establish properties using the https://microprofile.io/microprofile-config/[MicroProfile Config] specification. [cols="DynamoDB"] @@ -560,9 +560,10 @@ Please note that you can establish properties using the https://microprofile.io/ |`jnosql.dynamodb.secretaccess` |The AWS secret access key, used to authenticate the user interacting with AWS. - |=== +=== Using the Key-value API + This is an example using DynamoDB's Key-Value API with MicroProfile Config. [source,properties] @@ -571,6 +572,95 @@ jnosql.keyvalue.provider=org.eclipse.jnosql.databases.dynamodb.communication.Dyn jnosql.keyvalue.database=heroes ---- +=== Using the Document API + +The DynamoDB's Document API implementation follows the *SINGLE TABLE* strategy, it means, the table will store multiple entity types. To satisfy this strategy, the implementation assumes that the target table will have a composed primary key: + +- The `entityType` field as the partitioning key; +- The `id` field as the sort key; + +To customize the partitioning key field name, you can define the following configuration + +[source,properties] +---- +jnosql.dynamodb.entity.pk=entityType +---- + +By default, the implementation doesn't create the table on-the-fly, letting this requirement for the users. If you prefer, the implementation is able to create the table on-the-fly as well. To activate this capability you should define explicitly the following configuration: + +[source,properties] +---- +jnosql.dynamodb.create.tables=true +---- + +The table will be created with the composed primary key mentioned previously. + +Here's an example using DynamoDB's Document API with MicroProfile Config. + +[source,properties] +---- +jnosql.document.provider=org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentConfiguration +jnosql.document.database=heroes +---- + +The config settings are the default behavior; nevertheless, there is an option to do it programmatically. Create a class that implements the `Supplier` and then defines it as an `@Alternative` and the `Priority`. + +[source,java] +---- +@ApplicationScoped +@Alternative +@Priority(Interceptor.Priority.APPLICATION) +public class ManagerSupplier implements Supplier { + + @Produces + public DynamoDBDocumentManager get() { + Settings settings = Settings.builder().put("credential", "value").build(); + DynamoDBDocumentConfiguration configuration = new DynamoDBDocumentConfiguration(); + DynamoDBDocumentManagerFactory factory = configuration.apply(settings); + return factory.apply("database"); + } +} +---- + + +=== Repository + +The ```DynamoDBRepository``` interface is an extension of the ```Repository``` interface that allows execution of PartiQL via the ```@PartiQL``` annotation. + +WARNING: DynamoDB supports a limited subset of +https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.html[PartiQL]. + +NOTE: This implementation doesn't provide pagination on the queries. + +[source,java] +---- +@Repository +interface PersonRepository extends DynamoDBRepository { + +@PartiQL("select * from Person") +List findAll(); + +@PartiQL("select * from Person where name = ?") +List findByName(@Param("") String name); + +} +---- + + +=== Template + +The ```DynamoDBTemplate``` interface is a specialization of the ```DocumentTemplate``` interface that allows using PartiQL queries. + +WARNING: DynamoDB supports a limited subset of +https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.html[PartiQL]. + +NOTE: This implementation doesn't provide pagination on the queries. + +[source,java] +---- +List people = template.partiQL("select * from Person where name = ? ", params); +---- + == Elasticsearch image::https://jnosql.github.io/img/logos/elastic.svg[Elasticsearch Project,align="center"width=25%,height=25%] From 9cad0e6291b2b09ef245df2576da6a8302a93af5 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 06:45:07 -0300 Subject: [PATCH 67/70] build: removed duplicated dependencies declaration Signed-off-by: Maximillian Arruda --- jnosql-dynamodb/pom.xml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/jnosql-dynamodb/pom.xml b/jnosql-dynamodb/pom.xml index bc664215b..2d22e3d7e 100644 --- a/jnosql-dynamodb/pom.xml +++ b/jnosql-dynamodb/pom.xml @@ -48,17 +48,5 @@ dynamodb ${dynamodb.version} - - net.datafaker - datafaker - 2.0.2 - test - - - org.testcontainers - testcontainers - ${testcontainers.version} - test - \ No newline at end of file From 4a56b26779ef5f0d4fd73bbc9cf2484f1d53eb7f Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Mon, 29 Jan 2024 06:57:15 -0300 Subject: [PATCH 68/70] chore: removed unused imports Signed-off-by: Maximillian Arruda --- .../dynamodb/communication/DynamoDBDocumentManagerFactory.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java index 3edc940a1..b9e5b7840 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java @@ -16,7 +16,6 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.communication.document.DocumentManagerFactory; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; From f0ee8fb6b122bfaa9c5f1d729da75e0afdfcabc3 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Tue, 12 Mar 2024 15:05:34 -0300 Subject: [PATCH 69/70] chore: updated to use semistructured api Signed-off-by: Maximillian Arruda --- ...va => DefaultDynamoDBDatabaseManager.java} | 64 +++++---- .../communication/DynamoDBConverter.java | 40 +++--- ...ager.java => DynamoDBDatabaseManager.java} | 23 +--- ...va => DynamoDBDatabaseManagerFactory.java} | 10 +- .../DynamoDBDocumentConfiguration.java | 8 +- .../dynamodb/communication/DynamoDBQuery.java | 6 +- .../communication/DynamoDBQueryBuilder.java | 60 ++++----- .../DynamoDBQuerySelectBuilder.java | 22 ++-- .../mapping/DefaultDynamoDBTemplate.java | 39 +++--- .../mapping/DocumentManagerSupplier.java | 12 +- .../dynamodb/mapping/DynamoDBRepository.java | 4 +- .../mapping/DynamoDBRepositoryProxy.java | 12 +- .../dynamodb/mapping/DynamoDBTemplate.java | 8 +- ...tion.semistructured.DatabaseConfiguration} | 0 ...java => CommunicationEntityGenerator.java} | 20 +-- ...ltDynamoDBDatabaseManagerFactoryTest.java} | 17 ++- ...> DefaultDynamoDBDatabaseManagerTest.java} | 124 +++++++++--------- .../communication/DynamoDBConverterTest.java | 20 +-- .../DynamoDBDocumentConfigurationTest.java | 13 +- .../communication/DynamoDBTestUtils.java | 12 +- .../DynamoDBTemplateIntegrationTest.java | 7 +- .../mapping/DefaultDynamoDBTemplateTest.java | 30 ++--- .../mapping/DynamoDBExtensionTest.java | 5 +- .../mapping/DynamoDBRepositoryProxyTest.java | 4 +- .../dynamodb/mapping/MockProducer.java | 18 +-- 25 files changed, 277 insertions(+), 301 deletions(-) rename jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/{DefaultDynamoDBDocumentManager.java => DefaultDynamoDBDatabaseManager.java} (85%) rename jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/{DynamoDBDocumentManager.java => DynamoDBDatabaseManager.java} (60%) rename jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/{DynamoDBDocumentManagerFactory.java => DynamoDBDatabaseManagerFactory.java} (77%) rename jnosql-dynamodb/src/main/resources/META-INF/services/{org.eclipse.jnosql.communication.document.DocumentConfiguration => org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration} (100%) rename jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/{DocumentEntityGenerator.java => CommunicationEntityGenerator.java} (79%) rename jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/{DefaultDynamoDBDocumentManagerFactoryTest.java => DefaultDynamoDBDatabaseManagerFactoryTest.java} (72%) rename jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/{DefaultDynamoDBDocumentManagerTest.java => DefaultDynamoDBDatabaseManagerTest.java} (76%) diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManager.java similarity index 85% rename from jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java rename to jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManager.java index 9e235e2e4..9886d88fa 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManager.java @@ -16,9 +16,9 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; -import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.communication.document.DocumentQuery; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; @@ -60,11 +60,11 @@ import static java.util.Objects.requireNonNull; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.entityAttributeName; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toAttributeValue; -import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toDocumentEntity; +import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toCommunicationEntity; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toItem; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.toItemUpdate; -public class DefaultDynamoDBDocumentManager implements DynamoDBDocumentManager { +public class DefaultDynamoDBDatabaseManager implements DynamoDBDatabaseManager { private final String database; @@ -76,7 +76,7 @@ public class DefaultDynamoDBDocumentManager implements DynamoDBDocumentManager { private final ConcurrentHashMap tables = new ConcurrentHashMap<>(); - public DefaultDynamoDBDocumentManager(String database, DynamoDbClient dynamoDbClient, Settings settings) { + public DefaultDynamoDBDatabaseManager(String database, DynamoDbClient dynamoDbClient, Settings settings) { this.settings = settings; this.database = database; this.dynamoDbClient = dynamoDbClient; @@ -96,7 +96,7 @@ public String name() { } @Override - public DocumentEntity insert(DocumentEntity documentEntity) { + public CommunicationEntity insert(CommunicationEntity documentEntity) { requireNonNull(documentEntity, "documentEntity is required"); dynamoDbClient().putItem(PutItemRequest.builder() .tableName(createTableIfNeeded(documentEntity.name()).table().tableName()) @@ -189,7 +189,7 @@ private String getEntityAttributeName() { } @Override - public DocumentEntity insert(DocumentEntity documentEntity, Duration ttl) { + public CommunicationEntity insert(CommunicationEntity documentEntity, Duration ttl) { requireNonNull(documentEntity, "documentEntity is required"); requireNonNull(ttl, "ttl is required"); documentEntity.add(getTTLAttributeName(documentEntity.name()).get(), Instant.now().plus(ttl).truncatedTo(ChronoUnit.SECONDS)); @@ -197,7 +197,7 @@ public DocumentEntity insert(DocumentEntity documentEntity, Duration ttl) { } @Override - public Iterable insert(Iterable entities) { + public Iterable insert(Iterable entities) { requireNonNull(entities, "entities are required"); return StreamSupport.stream(entities.spliterator(), false) .map(this::insert) @@ -205,7 +205,7 @@ public Iterable insert(Iterable entities) { } @Override - public Iterable insert(Iterable entities, Duration ttl) { + public Iterable insert(Iterable entities, Duration ttl) { requireNonNull(entities, "entities is required"); requireNonNull(ttl, "ttl is required"); return StreamSupport.stream(entities.spliterator(), false) @@ -214,7 +214,7 @@ public Iterable insert(Iterable entities, Durati } @Override - public DocumentEntity update(DocumentEntity documentEntity) { + public CommunicationEntity update(CommunicationEntity documentEntity) { requireNonNull(documentEntity, "entity is required"); Map itemKey = getItemKey(documentEntity); Map attributeUpdates = asItemToUpdate(documentEntity); @@ -227,7 +227,7 @@ public DocumentEntity update(DocumentEntity documentEntity) { return documentEntity; } - private Map getItemKey(DocumentEntity documentEntity) { + private Map getItemKey(CommunicationEntity documentEntity) { DescribeTableResponse describeTableResponse = this.tables.computeIfAbsent(documentEntity.name(), this::getDescribeTableResponse); Map itemKey = describeTableResponse .table() @@ -243,12 +243,12 @@ private Map getItemKey(DocumentEntity documentEntity) { return itemKey; } - private Map asItemToUpdate(DocumentEntity documentEntity) { + private Map asItemToUpdate(CommunicationEntity documentEntity) { return toItemUpdate(this::resolveEntityNameAttributeName, documentEntity); } @Override - public Iterable update(Iterable entities) { + public Iterable update(Iterable entities) { requireNonNull(entities, "entities is required"); return StreamSupport.stream(entities.spliterator(), false) .map(this::update) @@ -256,34 +256,35 @@ public Iterable update(Iterable entities) { } @Override - public void delete(DocumentDeleteQuery documentDeleteQuery) { - Objects.requireNonNull(documentDeleteQuery, "documentDeleteQuery is required"); + public void delete(DeleteQuery deleteQuery) { + Objects.requireNonNull(deleteQuery, "deleteQuery is required"); - List primaryKeys = getDescribeTableResponse(documentDeleteQuery.name()) + List primaryKeys = getDescribeTableResponse(deleteQuery.name()) .table() .keySchema() .stream() .map(KeySchemaElement::attributeName).toList(); - DocumentQuery.DocumentQueryBuilder selectQueryBuilder = DocumentQuery.builder() + + var selectQueryBuilder = SelectQuery.builder() .select(primaryKeys.toArray(new String[0])) - .from(documentDeleteQuery.name()); + .from(deleteQuery.name()); - documentDeleteQuery.condition().ifPresent(selectQueryBuilder::where); + deleteQuery.condition().ifPresent(selectQueryBuilder::where); select(selectQueryBuilder.build()).forEach( documentEntity -> dynamoDbClient().deleteItem(DeleteItemRequest.builder() - .tableName(documentDeleteQuery.name()) + .tableName(deleteQuery.name()) .key(getItemKey(documentEntity)) .build())); } @Override - public Stream select(DocumentQuery documentQuery) { - Objects.requireNonNull(documentQuery, "documentQuery is required"); + public Stream select(SelectQuery query) { + Objects.requireNonNull(query, "query is required"); DynamoDBQuery dynamoDBQuery = DynamoDBQuery - .builderOf(documentQuery.name(), getEntityAttributeName(), documentQuery) + .builderOf(query.name(), getEntityAttributeName(), query) .get(); ScanRequest.Builder selectRequest = ScanRequest.builder() @@ -298,7 +299,7 @@ public Stream select(DocumentQuery documentQuery) { return StreamSupport .stream(dynamoDbClient().scanPaginator(selectRequest.build()).spliterator(), false) .flatMap(scanResponse -> scanResponse.items().stream() - .map(item -> toDocumentEntity(this::resolveEntityNameAttributeName, item))); + .map(item -> toCommunicationEntity(this::resolveEntityNameAttributeName, item))); } @Override @@ -319,12 +320,7 @@ public void close() { } @Override - public Stream partiQL(String query) { - return partiQL(query,new Object[0]); - } - - @Override - public Stream partiQL(String query, Object... params) { + public Stream partiQL(String query, Object... params) { Objects.requireNonNull(query, "query is required"); List parameters = Stream.of(params).map(DynamoDBConverter::toAttributeValue).toList(); ExecuteStatementResponse executeStatementResponse = dynamoDbClient() @@ -332,8 +328,8 @@ public Stream partiQL(String query, Object... params) { .statement(query) .parameters(parameters) .build()); - List result = new LinkedList<>(); - executeStatementResponse.items().forEach(item -> result.add(toDocumentEntity(this::resolveEntityNameAttributeName, item))); + List result = new LinkedList<>(); + executeStatementResponse.items().forEach(item -> result.add(toCommunicationEntity(this::resolveEntityNameAttributeName, item))); while (executeStatementResponse.nextToken() != null) { executeStatementResponse = dynamoDbClient() .executeStatement(ExecuteStatementRequest.builder() @@ -341,7 +337,7 @@ public Stream partiQL(String query, Object... params) { .parameters(parameters) .nextToken(executeStatementResponse.nextToken()) .build()); - executeStatementResponse.items().forEach(item -> result.add(toDocumentEntity(this::resolveEntityNameAttributeName, item))); + executeStatementResponse.items().forEach(item -> result.add(toCommunicationEntity(this::resolveEntityNameAttributeName, item))); } return result.stream(); } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java index 09876c31c..d233def3a 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverter.java @@ -15,9 +15,9 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.communication.driver.ValueUtil; +import org.eclipse.jnosql.communication.ValueUtil; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.Element; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.dynamodb.model.AttributeAction; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; @@ -62,7 +62,7 @@ private static Object convertValue(Object value) { case L: return attributeValue.l().stream().map(DynamoDBConverter::convertValue).toList(); case M: - return attributeValue.m().entrySet().stream().map(e -> Document.of(e.getKey(), convertValue(e.getValue()))).toList(); + return attributeValue.m().entrySet().stream().map(e -> Element.of(e.getKey(), convertValue(e.getValue()))).toList(); case NUL: return null; case BOOL: @@ -75,10 +75,10 @@ private static Object convertValue(Object value) { return value; } - static Map getMap(UnaryOperator entityNameResolver, DocumentEntity entity) { + static Map getMap(UnaryOperator entityNameResolver, CommunicationEntity entity) { var nameResolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); Map jsonObject = new HashMap<>(); - entity.documents().forEach(feedJSON(jsonObject)); + entity.elements().forEach(feedJSON(jsonObject)); jsonObject.put(entityAttributeName(nameResolver), entity.name()); return jsonObject; } @@ -88,11 +88,11 @@ public static String entityAttributeName(UnaryOperator nameResolver) { } @SuppressWarnings({"rawtypes", "unchecked"}) - private static Consumer feedJSON(Map jsonObject) { + private static Consumer feedJSON(Map jsonObject) { return d -> { Object value = ValueUtil.convert(d.value()); - if (value instanceof Document subDocument) { - jsonObject.put(d.name(), singletonMap(subDocument.name(), subDocument.get())); + if (value instanceof Element subElement) { + jsonObject.put(d.name(), singletonMap(subElement.name(), subElement.get())); } else if (isSudDocument(value)) { Map subDocument = getMap(value); jsonObject.put(d.name(), subDocument); @@ -114,7 +114,7 @@ private static Map getMap(Object value) { private static boolean isSudDocument(Object value) { return value instanceof Iterable && StreamSupport.stream(Iterable.class.cast(value).spliterator(), false). - allMatch(org.eclipse.jnosql.communication.document.Document.class::isInstance); + allMatch(Element.class::isInstance); } private static boolean isSudDocumentList(Object value) { @@ -122,9 +122,9 @@ private static boolean isSudDocumentList(Object value) { allMatch(d -> d instanceof Iterable && isSudDocument(d)); } - public static Map toItem(UnaryOperator entityNameResolver, DocumentEntity documentEntity) { + public static Map toItem(UnaryOperator entityNameResolver, CommunicationEntity entity) { UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); - Map documentAttributes = getMap(resolver, documentEntity); + Map documentAttributes = getMap(resolver, entity); return toItem(documentAttributes); } @@ -161,15 +161,15 @@ public static AttributeValue toAttributeValue(Object value) { if (value instanceof InputStream input) { return AttributeValue.builder().b(SdkBytes.fromInputStream(input)).build(); } - if (value instanceof Document document) { - return toAttributeValue(getMap(document)); + if (value instanceof Element element) { + return toAttributeValue(getMap(element)); } return AttributeValue.builder().s(String.valueOf(value)).build(); } - public static Map toItemUpdate(UnaryOperator entityNameResolver, DocumentEntity documentEntity) { + public static Map toItemUpdate(UnaryOperator entityNameResolver, CommunicationEntity entity) { UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); - Map documentAttributes = getMap(resolver, documentEntity); + Map documentAttributes = getMap(resolver, entity); return toItemUpdate(documentAttributes); } @@ -189,7 +189,7 @@ public static AttributeValueUpdate toAttributeValueUpdate(Object value) { } - public static DocumentEntity toDocumentEntity(UnaryOperator entityNameResolver, Map item) { + public static CommunicationEntity toCommunicationEntity(UnaryOperator entityNameResolver, Map item) { if (item == null) { return null; } @@ -199,11 +199,11 @@ public static DocumentEntity toDocumentEntity(UnaryOperator entityNameRe UnaryOperator resolver = Optional.ofNullable(entityNameResolver).orElse(UnaryOperator.identity()); String entityAttribute = resolver.apply(ENTITY); var entityName = item.containsKey(entityAttribute) ? item.get(entityAttribute).s() : entityAttribute; - List documents = item.entrySet() + var elements = item.entrySet() .stream() .filter(entry -> !Objects.equals(entityAttribute, entry.getKey())) - .map(entry -> Document.of(entry.getKey(), convertValue(entry.getValue()))) + .map(entry -> Element.of(entry.getKey(), convertValue(entry.getValue()))) .toList(); - return DocumentEntity.of(entityName, documents); + return CommunicationEntity.of(entityName, elements); } } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDatabaseManager.java similarity index 60% rename from jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java rename to jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDatabaseManager.java index 52b34df75..fca88ca2f 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDatabaseManager.java @@ -15,8 +15,8 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.communication.document.DocumentManager; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import java.util.stream.Stream; @@ -24,29 +24,18 @@ /** * A document manager interface for DynamoDB database operations. */ -public interface DynamoDBDocumentManager extends DocumentManager { - - /** - * DynamoDB supports a limited subset of - * PartiQL. - * This method executes a PartiQL query and returns a stream of DocumentEntity objects. - * - * @param query the PartiQL query - * @return a {@link Stream} of {@link DocumentEntity} representing the query result - * @throws NullPointerException when the query is null - */ - Stream partiQL(String query); +public interface DynamoDBDatabaseManager extends DatabaseManager { /** * DynamoDB supports a limited subset of PartiQL. - * This method executes a PartiQL query with parameters and returns a stream of DocumentEntity objects. + * This method executes a PartiQL query with parameters and returns a stream of CommunicationEntity objects. *

Example query: {@code SELECT * FROM users WHERE status = ?}

* * @param query the PartiQL query - * @return a {@link Stream} of {@link DocumentEntity} representing the query result + * @return a {@link Stream} of {@link CommunicationEntity} representing the query result * @throws NullPointerException when the query is null */ - Stream partiQL(String query, Object... params); + Stream partiQL(String query, Object... params); /** diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDatabaseManagerFactory.java similarity index 77% rename from jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java rename to jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDatabaseManagerFactory.java index b9e5b7840..2fddf20d9 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentManagerFactory.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDatabaseManagerFactory.java @@ -16,24 +16,24 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.communication.document.DocumentManagerFactory; +import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import java.util.Optional; -public class DynamoDBDocumentManagerFactory implements DocumentManagerFactory { +public class DynamoDBDatabaseManagerFactory implements DatabaseManagerFactory { private final DynamoDbClient dynamoDB; private final Settings settings; - public DynamoDBDocumentManagerFactory(DynamoDbClient dynamoDB, Settings settings) { + public DynamoDBDatabaseManagerFactory(DynamoDbClient dynamoDB, Settings settings) { this.dynamoDB = dynamoDB; this.settings = settings; } @Override - public DynamoDBDocumentManager apply(String database) { - return new DefaultDynamoDBDocumentManager(database, dynamoDB, settings); + public DynamoDBDatabaseManager apply(String database) { + return new DefaultDynamoDBDatabaseManager(database, dynamoDB, settings); } @Override diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java index 618838e0c..84b24f341 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfiguration.java @@ -16,13 +16,13 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.communication.document.DocumentConfiguration; +import org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration; public class DynamoDBDocumentConfiguration extends DynamoDBConfiguration - implements DocumentConfiguration { + implements DatabaseConfiguration { @Override - public DynamoDBDocumentManagerFactory apply(Settings settings) { + public DynamoDBDatabaseManagerFactory apply(Settings settings) { var dynamoDB = getDynamoDB(settings); - return new DynamoDBDocumentManagerFactory(dynamoDB, settings); + return new DynamoDBDatabaseManagerFactory(dynamoDB, settings); } } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuery.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuery.java index 26d544cd3..bad0a0a06 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuery.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuery.java @@ -15,7 +15,7 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.eclipse.jnosql.communication.document.DocumentQuery; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import java.util.Map; @@ -29,7 +29,7 @@ public record DynamoDBQuery(String table, public static Supplier builderOf(String table, String partitionKey, - DocumentQuery documentQuery) { - return new DynamoDBQuerySelectBuilder(table, partitionKey, documentQuery); + SelectQuery query) { + return new DynamoDBQuerySelectBuilder(table, partitionKey, query); } } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java index f93f6a072..0ead881d0 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQueryBuilder.java @@ -16,8 +16,8 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import org.eclipse.jnosql.communication.TypeReference; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentCondition; +import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; +import org.eclipse.jnosql.communication.semistructured.Element; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import java.util.ArrayList; @@ -31,66 +31,66 @@ abstract class DynamoDBQueryBuilder implements Supplier { - protected void condition(DocumentCondition condition, + protected void condition(CriteriaCondition condition, StringBuilder filterExpression, Map expressionAttributeNames, Map expressionAttributeValues) { - Document document = condition.document(); + var element = condition.element(); switch (condition.condition()) { case EQUALS: - predicate(" = ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + predicate(" = ", element, filterExpression, expressionAttributeNames, expressionAttributeValues); break; case LIKE: - predicateLike(document, filterExpression, expressionAttributeNames, expressionAttributeValues); + predicateLike(element, filterExpression, expressionAttributeNames, expressionAttributeValues); break; case IN: - predicateIn(document, filterExpression, expressionAttributeNames, expressionAttributeValues); + predicateIn(element, filterExpression, expressionAttributeNames, expressionAttributeValues); break; case GREATER_THAN: - predicate(" > ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + predicate(" > ", element, filterExpression, expressionAttributeNames, expressionAttributeValues); break; case LESSER_THAN: - predicate(" < ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + predicate(" < ", element, filterExpression, expressionAttributeNames, expressionAttributeValues); break; case GREATER_EQUALS_THAN: - predicate(" >= ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + predicate(" >= ", element, filterExpression, expressionAttributeNames, expressionAttributeValues); break; case LESSER_EQUALS_THAN: - predicate(" <= ", document, filterExpression, expressionAttributeNames, expressionAttributeValues); + predicate(" <= ", element, filterExpression, expressionAttributeNames, expressionAttributeValues); break; case BETWEEN: - predicateBetween(document, filterExpression, expressionAttributeNames, expressionAttributeValues); + predicateBetween(element, filterExpression, expressionAttributeNames, expressionAttributeValues); break; case AND: - appendCondition(document.get(new TypeReference<>() { + appendCondition(element.get(new TypeReference<>() { }), " AND ", filterExpression, expressionAttributeNames, expressionAttributeValues); break; case OR: - appendCondition(document.get(new TypeReference<>() { + appendCondition(element.get(new TypeReference<>() { }), " OR ", filterExpression, expressionAttributeNames, expressionAttributeValues); break; case NOT: filterExpression.append(" NOT "); - condition(document.get(DocumentCondition.class), filterExpression, expressionAttributeNames, expressionAttributeValues); + condition(element.get(CriteriaCondition.class), filterExpression, expressionAttributeNames, expressionAttributeValues); break; default: throw new IllegalArgumentException("Unknown condition " + condition.condition()); } } - private void predicateIn(Document document, + private void predicateIn(Element element, StringBuilder filterExpression, Map expressionAttributeNames, Map expressionAttributeValues) { - var name = document.name(); + var name = element.name(); var attributeName = "#" + name; expressionAttributeNames.put(attributeName, name); filterExpression.append(attributeName).append(" IN ("); List valuesExpressionNames = new LinkedList<>(); - ((Iterable) document.get()).forEach(value -> { + ((Iterable) element.get()).forEach(value -> { var attributeValueName = ":" + name + "_" + expressionAttributeValues.size(); valuesExpressionNames.add(attributeValueName); expressionAttributeValues.put(attributeValueName, toAttributeValue(value)); @@ -100,18 +100,18 @@ private void predicateIn(Document document, filterExpression.append(") "); } - private void appendCondition(List conditions, + private void appendCondition(List conditions, String condition, StringBuilder filterExpression, Map expressionAttributeNames, Map expressionAttributeValues) { boolean isFirstCondition = true; - for (DocumentCondition documentCondition : conditions) { + for (CriteriaCondition criteriaCondition : conditions) { StringBuilder tempFilterExpression = new StringBuilder(); HashMap tempExpressionAttributeNames = new HashMap<>(expressionAttributeNames); HashMap tempExpressionAttributeValues = new HashMap<>(expressionAttributeValues); - condition(documentCondition, + condition(criteriaCondition, tempFilterExpression, tempExpressionAttributeNames, tempExpressionAttributeValues); @@ -133,15 +133,15 @@ private void appendCondition(List conditions, } - private void predicateBetween(Document document, + private void predicateBetween(Element element, StringBuilder filterExpression, Map expressionAttributeNames, Map expressionAttributeValues) { - var name = document.name(); + var name = element.name(); List values = new ArrayList<>(); - ((Iterable) document.get()).forEach(values::add); + ((Iterable) element.get()).forEach(values::add); var attributeName = "#" + name; expressionAttributeNames.put(attributeName, name); @@ -158,13 +158,13 @@ private void predicateBetween(Document document, } - private void predicateLike(Document document, + private void predicateLike(Element element, StringBuilder filterExpression, Map expressionAttributeNames, Map expressionAttributeValues) { - var name = document.name(); - var value = toAttributeValue(document.get()); + var name = element.name(); + var value = toAttributeValue(element.get()); var attributeName = "#" + name; var attributeValueName = ":" + name + "_" + expressionAttributeValues.size(); @@ -178,13 +178,13 @@ private void predicateLike(Document document, } protected void predicate(String operator, - Document document, + Element element, StringBuilder filterExpression, Map expressionAttributeNames, Map expressionAttributeValues) { - var name = document.name(); - var value = toAttributeValue(document.get()); + var name = element.name(); + var value = toAttributeValue(element.get()); var attributeName = "#" + name; var attributeValueName = ":" + name + "_" + expressionAttributeValues.size(); diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuerySelectBuilder.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuerySelectBuilder.java index 48d4e1767..c0ae3d83b 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuerySelectBuilder.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBQuerySelectBuilder.java @@ -15,9 +15,9 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentCondition; -import org.eclipse.jnosql.communication.document.DocumentQuery; +import org.eclipse.jnosql.communication.semistructured.CriteriaCondition; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.communication.semistructured.SelectQuery; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; import java.util.HashMap; @@ -28,14 +28,14 @@ class DynamoDBQuerySelectBuilder extends DynamoDBQueryBuilder { private final String partitionKey; - private final DocumentQuery documentQuery; + private final SelectQuery selectQuery; public DynamoDBQuerySelectBuilder(String table, String partitionKey, - DocumentQuery documentQuery) { + SelectQuery selectQuery) { this.table = table; this.partitionKey = partitionKey; - this.documentQuery = documentQuery; + this.selectQuery = selectQuery; } @Override @@ -46,10 +46,10 @@ public DynamoDBQuery get() { var expressionAttributeValues = new HashMap(); super.condition( - DocumentCondition.eq(Document.of(partitionKey, documentQuery.name())), + CriteriaCondition.eq(Element.of(partitionKey, selectQuery.name())), filterExpression, expressionAttributeNames, expressionAttributeValues); - this.documentQuery.condition().ifPresent(c -> { + this.selectQuery.condition().ifPresent(c -> { filterExpression.append(" AND "); super.condition(c, filterExpression, @@ -67,11 +67,11 @@ public DynamoDBQuery get() { } String projectionExpression() { - var documents = documentQuery.documents(); - if (documents.isEmpty()) { + var columns = selectQuery.columns(); + if (columns.isEmpty()) { return null; } - return String.join(", ", documents); + return String.join(", ", columns); } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplate.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplate.java index 44ace7cd3..80f4aecba 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplate.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplate.java @@ -19,35 +19,35 @@ import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.Typed; import jakarta.inject.Inject; -import org.eclipse.jnosql.communication.document.DocumentManager; -import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentManager; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDatabaseManager; import org.eclipse.jnosql.mapping.core.Converters; -import org.eclipse.jnosql.mapping.document.AbstractDocumentTemplate; -import org.eclipse.jnosql.mapping.document.DocumentEntityConverter; -import org.eclipse.jnosql.mapping.document.DocumentEventPersistManager; import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; +import org.eclipse.jnosql.mapping.semistructured.AbstractSemistructuredTemplate; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; import java.util.Objects; import java.util.stream.Stream; @Typed(DynamoDBTemplate.class) @ApplicationScoped -class DefaultDynamoDBTemplate extends AbstractDocumentTemplate implements DynamoDBTemplate { +class DefaultDynamoDBTemplate extends AbstractSemistructuredTemplate implements DynamoDBTemplate { - private Instance manager; + private final Instance manager; - private DocumentEntityConverter converter; + private final EntityConverter converter; - private DocumentEventPersistManager persistManager; + private final EventPersistManager persistManager; - private EntitiesMetadata entitiesMetadata; + private final EntitiesMetadata entitiesMetadata; - private Converters converters; + private final Converters converters; @Inject - DefaultDynamoDBTemplate(Instance manager, - DocumentEntityConverter converter, - DocumentEventPersistManager persistManager, + DefaultDynamoDBTemplate(Instance manager, + EntityConverter converter, + EventPersistManager persistManager, EntitiesMetadata entitiesMetadata, Converters converters) { this.manager = manager; @@ -62,30 +62,31 @@ class DefaultDynamoDBTemplate extends AbstractDocumentTemplate implements Dynamo * Don't use it */ DefaultDynamoDBTemplate() { + this(null, null, null, null, null); } @Override - protected DocumentEntityConverter getConverter() { + protected EntityConverter converter() { return converter; } @Override - protected DocumentManager getManager() { + protected DatabaseManager manager() { return manager.get(); } @Override - protected DocumentEventPersistManager getEventManager() { + protected EventPersistManager eventManager() { return persistManager; } @Override - protected EntitiesMetadata getEntities() { + protected EntitiesMetadata entities() { return entitiesMetadata; } @Override - protected Converters getConverters() { + protected Converters converters() { return converters; } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DocumentManagerSupplier.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DocumentManagerSupplier.java index ffee5d86a..8013b9485 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DocumentManagerSupplier.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DocumentManagerSupplier.java @@ -22,7 +22,7 @@ import jakarta.enterprise.inject.Typed; import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentConfiguration; -import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentManager; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDatabaseManager; import org.eclipse.jnosql.mapping.core.config.MicroProfileSettings; import java.util.Optional; @@ -33,22 +33,22 @@ import static org.eclipse.jnosql.mapping.core.config.MappingConfigurations.DOCUMENT_DATABASE; @ApplicationScoped -public class DocumentManagerSupplier implements Supplier { +public class DocumentManagerSupplier implements Supplier { private static final Logger LOGGER = Logger.getLogger(DocumentManagerSupplier.class.getName()); @Override @Produces - @Typed(DynamoDBDocumentManager.class) + @Typed(DynamoDBDatabaseManager.class) @ApplicationScoped - public DynamoDBDocumentManager get() { + public DynamoDBDatabaseManager get() { Settings settings = MicroProfileSettings.INSTANCE; var configuration = new DynamoDBDocumentConfiguration(); var factory = configuration.apply(settings); Optional database = settings.get(DOCUMENT_DATABASE, String.class); String db = database.orElseThrow(() -> new MappingException("Please, inform the database filling up the property " + DOCUMENT_DATABASE.get())); - DynamoDBDocumentManager manager = (DynamoDBDocumentManager) factory.apply(db); + DynamoDBDatabaseManager manager = (DynamoDBDatabaseManager) factory.apply(db); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, """ Starting a DynamoDBDocumentManager instance using Eclipse MicroProfile Config,\ @@ -58,7 +58,7 @@ public DynamoDBDocumentManager get() { return manager; } - public void close(@Disposes DynamoDBDocumentManager manager) { + public void close(@Disposes DynamoDBDatabaseManager manager) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, "Closing OracleDocumentManager resource, database name: %s".formatted(manager.name())); } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepository.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepository.java index 122df784a..7d741cd0c 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepository.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepository.java @@ -15,7 +15,7 @@ package org.eclipse.jnosql.databases.dynamodb.mapping; -import jakarta.data.repository.BasicRepository; +import org.eclipse.jnosql.mapping.NoSQLRepository; -public interface DynamoDBRepository extends BasicRepository { +public interface DynamoDBRepository extends NoSQLRepository { } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxy.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxy.java index b65b80bf7..6cb9cd3d4 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxy.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxy.java @@ -20,11 +20,11 @@ import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.core.query.AbstractRepository; import org.eclipse.jnosql.mapping.core.repository.DynamicReturn; -import org.eclipse.jnosql.mapping.document.JNoSQLDocumentTemplate; -import org.eclipse.jnosql.mapping.document.query.AbstractDocumentRepositoryProxy; -import org.eclipse.jnosql.mapping.document.query.DocumentRepositoryProxy; import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; import org.eclipse.jnosql.mapping.metadata.EntityMetadata; +import org.eclipse.jnosql.mapping.semistructured.SemistructuredTemplate; +import org.eclipse.jnosql.mapping.semistructured.query.AbstractSemistructuredRepositoryProxy; +import org.eclipse.jnosql.mapping.semistructured.query.SemistructuredRepositoryProxy; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -37,7 +37,7 @@ import static org.eclipse.jnosql.mapping.core.repository.DynamicReturn.toSingleResult; -class DynamoDBRepositoryProxy extends AbstractDocumentRepositoryProxy { +class DynamoDBRepositoryProxy extends AbstractSemistructuredRepositoryProxy { private final DynamoDBTemplate template; @@ -63,7 +63,7 @@ class DynamoDBRepositoryProxy extends AbstractDocumentRepositoryProxy) ((ParameterizedType) type.getGenericInterfaces()[0]).getActualTypeArguments()[0]; this.converters = converters; this.entityMetadata = entitiesMetadata.get(typeClass); - this.repository = DocumentRepositoryProxy.DocumentRepository.of(template, entityMetadata); + this.repository = SemistructuredRepositoryProxy.SemistructuredRepository.of(template, entityMetadata); } /** @@ -143,7 +143,7 @@ protected EntityMetadata entityMetadata() { } @Override - protected JNoSQLDocumentTemplate template() { + protected SemistructuredTemplate template() { return template; } } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java index 44d16310a..f766110e6 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java @@ -16,12 +16,12 @@ package org.eclipse.jnosql.databases.dynamodb.mapping; import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.mapping.document.JNoSQLDocumentTemplate; +import org.eclipse.jnosql.mapping.document.DocumentTemplate; import java.util.stream.Stream; /** - * The {@code DynamoDBTemplate} is an interface that extends {@link JNoSQLDocumentTemplate} and + * The {@code DynamoDBTemplate} is an interface that extends {@link DocumentTemplate} and * provides methods for executing Dynamo DB queries using the PartiQL Language. *

DynamoDB supports a limited subset of * PartiQL. @@ -31,9 +31,9 @@ * process data in a more flexible and customizable way. *

* - * @see JNoSQLDocumentTemplate + * @see DocumentTemplate */ -public interface DynamoDBTemplate extends JNoSQLDocumentTemplate { +public interface DynamoDBTemplate extends DocumentTemplate { /** * Executes a DynamoDB query using diff --git a/jnosql-dynamodb/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.document.DocumentConfiguration b/jnosql-dynamodb/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration similarity index 100% rename from jnosql-dynamodb/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.document.DocumentConfiguration rename to jnosql-dynamodb/src/main/resources/META-INF/services/org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/CommunicationEntityGenerator.java similarity index 79% rename from jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java rename to jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/CommunicationEntityGenerator.java index ae364826f..e5a72205f 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DocumentEntityGenerator.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/CommunicationEntityGenerator.java @@ -16,9 +16,9 @@ package org.eclipse.jnosql.databases.dynamodb.communication; import net.datafaker.Faker; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.communication.document.Documents; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.communication.semistructured.Elements; import org.jetbrains.annotations.NotNull; import java.math.BigDecimal; @@ -30,25 +30,25 @@ import java.util.UUID; import java.util.function.Consumer; -final class DocumentEntityGenerator { +final class CommunicationEntityGenerator { static final String COLLECTION_NAME = "entityA"; static final Faker faker = new Faker(); - static DocumentEntity createRandomEntity() { + static CommunicationEntity createRandomEntity() { return createRandomEntityWithSubDocuments(0); } - static DocumentEntity createRandomEntity(String collectionName) { + static CommunicationEntity createRandomEntity(String collectionName) { return createRandomEntityWithSubDocuments(collectionName,0); } - static DocumentEntity createRandomEntityWithSubDocuments(int levels) { + static CommunicationEntity createRandomEntityWithSubDocuments(int levels) { return createRandomEntityWithSubDocuments(COLLECTION_NAME, levels); } @NotNull - private static DocumentEntity createRandomEntityWithSubDocuments(String collectionName, int levels) { + private static CommunicationEntity createRandomEntityWithSubDocuments(String collectionName, int levels) { Map map = new HashMap<>(); map.put(DynamoDBConverter.ID, UUID.randomUUID().toString()); map.put("name", faker.name().firstName()); @@ -61,8 +61,8 @@ private static DocumentEntity createRandomEntityWithSubDocuments(String collecti if (levels > 0) { addSubDocument(m -> map.put("level" + levels, m), levels - 1); } - DocumentEntity entity = DocumentEntity.of(collectionName); - List documents = Documents.of(map); + var entity = CommunicationEntity.of(collectionName); + List documents = Elements.of(map); documents.forEach(entity::add); return entity; } diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerFactoryTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManagerFactoryTest.java similarity index 72% rename from jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerFactoryTest.java rename to jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManagerFactoryTest.java index faf6a8cfa..08b2b8ece 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerFactoryTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManagerFactoryTest.java @@ -15,9 +15,8 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.eclipse.jnosql.communication.document.DocumentManagerFactory; +import org.eclipse.jnosql.communication.semistructured.DatabaseManagerFactory; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; @@ -28,25 +27,25 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) -class DefaultDynamoDBDocumentManagerFactoryTest { +class DefaultDynamoDBDatabaseManagerFactoryTest { - private DocumentManagerFactory documentManagerFactory; + private DatabaseManagerFactory databaseManagerFactory; @BeforeEach void setup() { - this.documentManagerFactory = DynamoDBTestUtils.CONFIG.getDocumentManagerFactory(); + this.databaseManagerFactory = DynamoDBTestUtils.CONFIG.getDocumentManagerFactory(); assertSoftly(softly -> { - softly.assertThat(documentManagerFactory).isNotNull(); - softly.assertThat(documentManagerFactory).isInstanceOf(DynamoDBDocumentManagerFactory.class); + softly.assertThat(databaseManagerFactory).isNotNull(); + softly.assertThat(databaseManagerFactory).isInstanceOf(DynamoDBDatabaseManagerFactory.class); }); } @AfterEach void tearDown() { - assertDoesNotThrow(documentManagerFactory::close, "DocumentManagerFactory.close() should be not throw exceptions"); + assertDoesNotThrow(databaseManagerFactory::close, "DocumentManagerFactory.close() should be not throw exceptions"); } @Test void shouldCreateDocumentManager() { - var documentManager = documentManagerFactory.apply("anydatabase"); + var documentManager = databaseManagerFactory.apply("anydatabase"); assertSoftly(softly -> { softly.assertThat(documentManager).isNotNull(); }); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManagerTest.java similarity index 76% rename from jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java rename to jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManagerTest.java index cd6cfcb4c..7f7fd303c 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDocumentManagerTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManagerTest.java @@ -20,9 +20,9 @@ import org.assertj.core.api.Assertions; import org.assertj.core.api.SoftAssertions; import org.eclipse.jnosql.communication.Settings; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.communication.document.DocumentManager; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.DatabaseManager; +import org.eclipse.jnosql.communication.semistructured.Element; import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -44,16 +44,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.eclipse.jnosql.communication.document.DocumentDeleteQuery.delete; -import static org.eclipse.jnosql.communication.document.DocumentQuery.select; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; +import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete; +import static org.eclipse.jnosql.communication.semistructured.SelectQuery.select; +import static org.eclipse.jnosql.databases.dynamodb.communication.CommunicationEntityGenerator.createRandomEntity; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBConverter.ID; -import static org.eclipse.jnosql.databases.dynamodb.communication.DocumentEntityGenerator.createRandomEntity; import static org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils.CONFIG; @EnabledIfSystemProperty(named = NAMED, matches = MATCHES) -class DefaultDynamoDBDocumentManagerTest { +class DefaultDynamoDBDatabaseManagerTest { private static Faker faker = new Faker(); @@ -69,7 +69,7 @@ void setUp() { tearDown(); } - private DocumentManager getDocumentManagerCannotCreateTables() { + private DatabaseManager getDatabaseManagerCannotCreateTables() { var settings = CONFIG.customSetting(Settings.builder() .put(DynamoDBConfigurations.CREATE_TABLES, "false")); var database = settings.get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); @@ -77,7 +77,7 @@ private DocumentManager getDocumentManagerCannotCreateTables() { return documentManagerFactory.apply(database); } - private DocumentManager getDocumentManagerCanCreateTables() { + private DatabaseManager getDatabaseManagerCanCreateTables() { var settings = CONFIG.customSetting(Settings.builder() .put(DynamoDBConfigurations.CREATE_TABLES, "true")); var database = settings.get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); @@ -97,7 +97,7 @@ void tearDown() { @Test void shouldReturnName() { - try (var manager = getDocumentManagerCannotCreateTables()) { + try (var manager = getDatabaseManagerCannotCreateTables()) { var database = CONFIG .getSettings() .get(MappingConfigurations.DOCUMENT_DATABASE, String.class).orElseThrow(); @@ -108,27 +108,27 @@ void shouldReturnName() { @Test void shouldReturnErrorWhenInsertWithInvalidInputs() { - try (var documentManager = getDocumentManagerCannotCreateTables()) { + try (var manager = getDatabaseManagerCannotCreateTables()) { assertSoftly(softly -> { - softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null)) + softly.assertThatThrownBy(() -> manager.insert((CommunicationEntity) null)) .as("should return error when insert a null DocumentEntity reference") .isExactlyInstanceOf(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, Duration.ofSeconds(1))) + softly.assertThatThrownBy(() -> manager.insert((CommunicationEntity) null, Duration.ofSeconds(1))) .as("should return error when insert a null DocumentEntity reference with TTL param") .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((DocumentEntity) null, null)) + softly.assertThatThrownBy(() -> manager.insert((CommunicationEntity) null, null)) .as("should return error when insert a null DocumentEntity reference with nullable TTL param") .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert(DocumentEntityGenerator.createRandomEntity(), null)) + softly.assertThatThrownBy(() -> manager.insert(CommunicationEntityGenerator.createRandomEntity(), null)) .as("should return error when insert a null DocumentEntity reference with nullable TTL param") .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null)) + softly.assertThatThrownBy(() -> manager.insert((Iterable) null)) .as("should return error when insert a null Iterable reference") .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert((Iterable) null, Duration.ofSeconds(1))) + softly.assertThatThrownBy(() -> manager.insert((Iterable) null, Duration.ofSeconds(1))) .as("should return error when insert a null Iterable reference with TTL param") .isInstanceOfAny(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.insert(List.of(DocumentEntityGenerator.createRandomEntity()), null)) + softly.assertThatThrownBy(() -> manager.insert(List.of(CommunicationEntityGenerator.createRandomEntity()), null)) .as("should return error when insert a null Iterable reference with nullable TTL param") .isInstanceOfAny(NullPointerException.class); }); @@ -138,16 +138,16 @@ void shouldReturnErrorWhenInsertWithInvalidInputs() { @Test void shouldInsert() { - try (var documentManager = getDocumentManagerCanCreateTables()) { + try (var manager = getDatabaseManagerCanCreateTables()) { assertSoftly(softly -> { - DocumentEntity entity = createRandomEntity(); + var entity = createRandomEntity(); var _entityType = entity.name(); var id = entity.find(DynamoDBConverter.ID, String.class).orElseThrow(); - var persistedEntity = documentManager.insert(entity); + var persistedEntity = manager.insert(entity); softly.assertThat(persistedEntity) - .as("documentManager.insert(DocumentEntity) method should return a non-null persistent DocumentEntity") + .as("manager.insert(DocumentEntity) method should return a non-null persistent DocumentEntity") .isNotNull(); var persistedItem = getItem(_entityType, id); @@ -157,9 +157,9 @@ void shouldInsert() { assertSoftly(softly -> { var entities = List.of(createRandomEntity(), createRandomEntity(), createRandomEntity()); - Iterable persistedEntities = documentManager.insert(entities); + var persistedEntities = manager.insert(entities); softly.assertThat(persistedEntities) - .as("documentManager.insert(Iterable<>) should returns the non-null list of DocumentEntity").isNotNull(); + .as("manager.insert(Iterable<>) should returns the non-null list of DocumentEntity").isNotNull(); assertThat(persistedEntities) .as("documentmanager.insert(iterable<>) should returns a corresponded list of DocumentEntity") @@ -181,12 +181,12 @@ void shouldInsert() { @Test void shouldReturnErrorWhenUpdateWithInvalidInputs() { - try (var documentManager = getDocumentManagerCannotCreateTables()) { + try (var manager = getDatabaseManagerCannotCreateTables()) { assertSoftly(softly -> { - softly.assertThatThrownBy(() -> documentManager.update((DocumentEntity) null)) + softly.assertThatThrownBy(() -> manager.update((CommunicationEntity) null)) .as("should return error when insert a null DocumentEntity reference") .isExactlyInstanceOf(NullPointerException.class); - softly.assertThatThrownBy(() -> documentManager.update((Iterable) null)) + softly.assertThatThrownBy(() -> manager.update((Iterable) null)) .as("should return error when insert a null Iterable reference") .isInstanceOfAny(NullPointerException.class); }); @@ -195,15 +195,15 @@ void shouldReturnErrorWhenUpdateWithInvalidInputs() { @Test void shouldUpdate() { - try (var documentManager = getDocumentManagerCanCreateTables()) { + try (var manager = getDatabaseManagerCanCreateTables()) { var entity1 = createRandomEntity(); var entity2 = createRandomEntity(); var entity3 = createRandomEntity(); - documentManager.insert(List.of(entity1, entity2, entity3)); + manager.insert(List.of(entity1, entity2, entity3)); - final BiConsumer assertions = (softly, updatedEntity) -> { + final BiConsumer assertions = (softly, updatedEntity) -> { Map item = getItem(updatedEntity.name(), updatedEntity.find(DynamoDBConverter.ID, String.class).orElseThrow()); softly.assertThat(item.get("name")) .as("the name attribute should exists in the returned item from dynamodb") @@ -214,25 +214,25 @@ void shouldUpdate() { }; assertSoftly(softly -> { - entity1.add(Document.of("name", faker.name().fullName())); - var updatedEntity = documentManager.update(entity1); + entity1.add(Element.of("name", faker.name().fullName())); + var updatedEntity = manager.update(entity1); softly.assertThat(updatedEntity) - .as("documentManager.update(DocumentEntity) method should return a non-null persistent DocumentEntity") + .as("manager.update(DocumentEntity) method should return a non-null persistent DocumentEntity") .isNotNull(); assertions.accept(softly, updatedEntity); }); assertSoftly(softly -> { - entity2.add(Document.of("name", faker.name().fullName())); - entity3.add(Document.of("name", faker.name().fullName())); + entity2.add(Element.of("name", faker.name().fullName())); + entity3.add(Element.of("name", faker.name().fullName())); - var updatedEntities = documentManager.update(List.of(entity2, entity2)); + var updatedEntities = manager.update(List.of(entity2, entity2)); softly.assertThat(updatedEntities) - .as("documentManager.update(Iterable<>) method should return a non-null list of DocumentEntity") + .as("manager.update(Iterable<>) method should return a non-null list of DocumentEntity") .isNotNull(); softly.assertThat(updatedEntities) .as("the size of the returned list of DocumentEntity from " + - "documentManager.update(Iterable<>) method should be equals to the size of the submitted list of DocumentEntity") + "manager.update(Iterable<>) method should be equals to the size of the submitted list of DocumentEntity") .hasSize(2); updatedEntities.forEach(updatedEntity -> assertions.accept(softly, updatedEntity)); }); @@ -255,12 +255,12 @@ private Map getItem(String _entityType, String id) { @Test void shouldCountByCollectionName() { - try (var dmCanCreateTable = getDocumentManagerCanCreateTables(); - var dmCannotCreateTable = getDocumentManagerCannotCreateTables()) { + try (var dmCanCreateTable = getDatabaseManagerCanCreateTables(); + var dmCannotCreateTable = getDatabaseManagerCannotCreateTables()) { assertSoftly(softly -> { - DocumentEntity entity = createRandomEntity(); - DocumentEntity entity2 = createRandomEntity(); + var entity = createRandomEntity(); + var entity2 = createRandomEntity(); dmCanCreateTable.insert(entity); dmCanCreateTable.insert(entity2); @@ -288,7 +288,7 @@ void shouldCountByCollectionName() { .isInstanceOfAny(ResourceNotFoundException.class); var entityBName = "entityB"; - DocumentEntity entity3 = createRandomEntity(entityBName); + var entity3 = createRandomEntity(entityBName); dmCanCreateTable.insert(entity3); softly.assertThat(dmCannotCreateTable.count(entity3.name())) @@ -301,9 +301,9 @@ void shouldCountByCollectionName() { @Test void shouldDelete() { - try (var documentManager = getDocumentManagerCanCreateTables()) { + try (var manager = getDatabaseManagerCanCreateTables()) { - DocumentEntity entity1, entity2, entity3, entity4; + CommunicationEntity entity1, entity2, entity3, entity4; var entities = List.of( entity1 = createRandomEntity(), @@ -311,7 +311,7 @@ void shouldDelete() { entity3 = createRandomEntity(), entity4 = createRandomEntity()); - documentManager.insert(entities); + manager.insert(entities); var entityType = entity1.name(); var id1 = entity1.find(ID, String.class).orElseThrow(); @@ -321,13 +321,13 @@ void shouldDelete() { assertSoftly(softly -> { - documentManager.delete(delete(). + manager.delete(delete(). from(entityType) .where(ID).eq(id1) .build() ); - softly.assertThat(documentManager.count(entityType)) + softly.assertThat(manager.count(entityType)) .as("the returned count number of items from a given table name is incorrect") .isEqualTo(entities.size() - 1L); @@ -335,13 +335,13 @@ void shouldDelete() { .as("the item should be deleted") .hasSize(0); - documentManager.delete(delete(). + manager.delete(delete(). from(entityType) .where(ID).in(List.of(id2, id3)) .build() ); - softly.assertThat(documentManager.count(entityType)) + softly.assertThat(manager.count(entityType)) .as("the returned count number of items from a given table name is incorrect") .isEqualTo(entities.size() - 3L); @@ -354,7 +354,7 @@ void shouldDelete() { .hasSize(0); - documentManager.delete(delete(). + manager.delete(delete(). from(entityType) .build() ); @@ -363,7 +363,7 @@ void shouldDelete() { .as("the item should be deleted") .hasSize(0); - softly.assertThat(documentManager.count(entityType)) + softly.assertThat(manager.count(entityType)) .as("the returned count number of items from a given table name is incorrect") .isEqualTo(0); @@ -375,13 +375,13 @@ void shouldDelete() { @Test void shouldCountByDocumentQuery() { - try (var documentManager = getDocumentManagerCanCreateTables()) { + try (var manager = getDatabaseManagerCanCreateTables()) { - DocumentEntity entity1, entity2, entity3; + CommunicationEntity entity1, entity2, entity3; var entities = List.of(entity1 = createRandomEntity(), entity2 = createRandomEntity(), entity3 = createRandomEntity()); - documentManager.insert(entities); + manager.insert(entities); assertSoftly(softly -> { @@ -390,7 +390,7 @@ void shouldCountByDocumentQuery() { .where(ID).eq(entity1.find(ID, String.class).orElseThrow()) .build(); - softly.assertThat(documentManager.count(documentQuery1)) + softly.assertThat(manager.count(documentQuery1)) .as("the returned count number of items from a given DocumentQuery is incorrect") .isEqualTo(1L); @@ -401,7 +401,7 @@ void shouldCountByDocumentQuery() { .or(ID).eq(entity2.find(ID, String.class).orElseThrow()) .build(); - softly.assertThat(documentManager.count(documentQuery2)) + softly.assertThat(manager.count(documentQuery2)) .as("the returned count number of items from a given DocumentQuery is incorrect") .isEqualTo(2L); @@ -412,7 +412,7 @@ void shouldCountByDocumentQuery() { .or(ID).eq(entity3.find(ID, String.class).orElseThrow()) .build(); - softly.assertThat(documentManager.count(documentQuery3)) + softly.assertThat(manager.count(documentQuery3)) .as("the returned count number of items from a given DocumentQuery is incorrect") .isEqualTo(3L); @@ -423,13 +423,13 @@ void shouldCountByDocumentQuery() { @Test void shouldExecutePartiQL() { - try (var documentManager = getDocumentManagerCanCreateTables()) { + try (var manager = getDatabaseManagerCanCreateTables()) { - DocumentEntity entity1; + CommunicationEntity entity1; var entities = List.of(entity1 = createRandomEntity(), createRandomEntity(), createRandomEntity()); - documentManager.insert(entities); + manager.insert(entities); - if (documentManager instanceof DynamoDBDocumentManager partiManager) { + if (manager instanceof DynamoDBDatabaseManager partiManager) { assertSoftly(softly -> { softly.assertThat(partiManager.partiQL("SELECT * FROM " + entity1.name())) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java index c734c4d99..1b7404cfb 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java @@ -21,6 +21,8 @@ import org.eclipse.jnosql.communication.document.Document; import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.driver.JsonbSupplier; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.Element; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; @@ -46,20 +48,20 @@ class DynamoDBConverterTest { void shouldConvertToItemRequest() { assertSoftly(softly -> { - var entity = DocumentEntity.of("entityA", + var entity = CommunicationEntity.of("entityA", List.of( - Document.of("_id", UUID.randomUUID().toString()), - Document.of("city", faker.address().city()), - Document.of("total", 10.0), - Document.of("address", List.of( - Document.of("zipcode", faker.address().zipCode()), - Document.of("city", faker.address().cityName()))), - Document.of("phones", List.of(faker.name().firstName(), faker.name().firstName(), faker.name().firstName())) + Element.of("_id", UUID.randomUUID().toString()), + Element.of("city", faker.address().city()), + Element.of("total", 10.0), + Element.of("address", List.of( + Element.of("zipcode", faker.address().zipCode()), + Element.of("city", faker.address().cityName()))), + Element.of("phones", List.of(faker.name().firstName(), faker.name().firstName(), faker.name().firstName())) )); var item = DynamoDBConverter.toItem(entityNameResolver, entity); - var entityFromItem = DynamoDBConverter.toDocumentEntity(entityNameResolver, item); + var entityFromItem = DynamoDBConverter.toCommunicationEntity(entityNameResolver, item); var expected = Json.createReader(new StringReader(JSONB.toJson(DynamoDBConverter.getMap(entityNameResolver, entity)))).readObject(); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java index af7fe0353..10b9122e0 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBDocumentConfigurationTest.java @@ -15,8 +15,7 @@ package org.eclipse.jnosql.databases.dynamodb.communication; -import org.eclipse.jnosql.communication.document.DocumentConfiguration; -import org.eclipse.jnosql.communication.document.DocumentManagerFactory; +import org.eclipse.jnosql.communication.semistructured.DatabaseConfiguration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; @@ -30,21 +29,21 @@ class DynamoDBDocumentConfigurationTest { @Test void shouldReturnFromServiceLoaderConfiguration() { - var configuration = DocumentConfiguration.getConfiguration(); + var configuration = DatabaseConfiguration.getConfiguration(); Assertions.assertNotNull(configuration); - Assertions.assertInstanceOf(DynamoDBDocumentConfiguration.class, configuration); + Assertions.assertInstanceOf(DatabaseConfiguration.class, configuration); } @Test void shouldReturnFromServiceLoaderConfigurationQuery() { - var configuration = DocumentConfiguration + var configuration = DatabaseConfiguration .getConfiguration(DynamoDBDocumentConfiguration.class); Assertions.assertNotNull(configuration); } @Test void shouldReturnDocumentManagerFactory() { - var configuration = DocumentConfiguration + var configuration = DatabaseConfiguration .getConfiguration(DynamoDBDocumentConfiguration.class); var settings = DynamoDBTestUtils.CONFIG.getSettings(); @@ -54,7 +53,7 @@ void shouldReturnDocumentManagerFactory() { .describedAs("DocumentConfiguration.getConfiguration(DynamoDBDocumentConfiguration.class) must return a non-null instance") .isNotNull(); - DocumentManagerFactory documentManagerFactory = configuration.apply(settings); + DynamoDBDatabaseManagerFactory documentManagerFactory = configuration.apply(settings); softly.assertThat(documentManagerFactory) .describedAs("DynamoDBDocumentConfiguration.apply(Settings.class) should returns a non-null DocumentManagerFactory instance") diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java index f0f7b080d..66c593c4c 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBTestUtils.java @@ -16,7 +16,6 @@ import org.eclipse.jnosql.communication.Settings; import org.eclipse.jnosql.communication.SettingsBuilder; -import org.eclipse.jnosql.communication.document.DocumentManager; import org.eclipse.jnosql.communication.keyvalue.BucketManagerFactory; import org.eclipse.jnosql.mapping.core.config.MappingConfigurations; import org.jetbrains.annotations.NotNull; @@ -46,23 +45,16 @@ private static DynamoDBBucketManagerFactory getBucketManagerFactory(Settings set return configuration.apply(settings); } - DynamoDBDocumentManagerFactory getDocumentManagerFactory() { + DynamoDBDatabaseManagerFactory getDocumentManagerFactory() { Settings settings = getSettings(); return getDocumentManagerFactory(settings); } - DynamoDBDocumentManagerFactory getDocumentManagerFactory(Settings settings) { + DynamoDBDatabaseManagerFactory getDocumentManagerFactory(Settings settings) { var configuration = new DynamoDBDocumentConfiguration(); return configuration.apply(settings); } - DocumentManager getDocumentManager(Settings settings) { - var database = settings - .get(MappingConfigurations.DOCUMENT_DATABASE, String.class) - .orElseThrow(); - return getDocumentManagerFactory(settings).apply(database); - } - public Settings getSettings() { dynamodb.start(); String dynamoDBHost = getDynamoDBHost(dynamodb.getHost(), dynamodb.getFirstMappedPort()); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/DynamoDBTemplateIntegrationTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/DynamoDBTemplateIntegrationTest.java index bcb2eb116..6d9b4684a 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/DynamoDBTemplateIntegrationTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/integration/DynamoDBTemplateIntegrationTest.java @@ -21,12 +21,11 @@ import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBTestUtils; import org.eclipse.jnosql.databases.dynamodb.mapping.DynamoDBExtension; import org.eclipse.jnosql.databases.dynamodb.mapping.DynamoDBTemplate; -import org.eclipse.jnosql.mapping.Convert; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -import org.eclipse.jnosql.mapping.document.DocumentEntityConverter; import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; import org.jboss.weld.junit5.auto.AddExtensions; import org.jboss.weld.junit5.auto.AddPackages; import org.jboss.weld.junit5.auto.EnableAutoWeld; @@ -36,13 +35,13 @@ import java.util.Optional; import static java.util.UUID.randomUUID; -import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES; import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED; @EnableAutoWeld -@AddPackages(value = {Convert.class, DocumentEntityConverter.class}) +@AddPackages(value = {Converters.class, EntityConverter.class}) @AddPackages(Book.class) @AddPackages(DynamoDBTemplate.class) @AddExtensions({EntityMetadataExtension.class, DocumentExtension.class, DynamoDBExtension.class}) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplateTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplateTest.java index 8ee5ba532..a34399d4a 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplateTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DefaultDynamoDBTemplateTest.java @@ -18,15 +18,15 @@ import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import org.assertj.core.api.SoftAssertions; -import org.eclipse.jnosql.communication.document.DocumentDeleteQuery; -import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentManager; +import org.eclipse.jnosql.communication.semistructured.DeleteQuery; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDatabaseManager; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -import org.eclipse.jnosql.mapping.document.DocumentEntityConverter; -import org.eclipse.jnosql.mapping.document.DocumentEventPersistManager; import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; +import org.eclipse.jnosql.mapping.semistructured.EventPersistManager; import org.jboss.weld.junit5.auto.AddExtensions; import org.jboss.weld.junit5.auto.AddPackages; import org.jboss.weld.junit5.auto.EnableAutoWeld; @@ -42,7 +42,7 @@ import static org.mockito.Mockito.when; @EnableAutoWeld -@AddPackages(value = {Converters.class, DocumentEntityConverter.class, PartiQL.class}) +@AddPackages(value = {Converters.class, EntityConverter.class, PartiQL.class}) @AddPackages(MockProducer.class) @AddExtensions({EntityMetadataExtension.class, DocumentExtension.class, DynamoDBExtension.class}) @ExtendWith(MockitoExtension.class) @@ -50,10 +50,10 @@ class DefaultDynamoDBTemplateTest { @Inject - private DocumentEntityConverter converter; + private EntityConverter converter; @Inject - private DocumentEventPersistManager persistManager; + private EventPersistManager persistManager; @Inject private EntitiesMetadata entities; @@ -61,39 +61,39 @@ class DefaultDynamoDBTemplateTest { @Inject private Converters converters; - private DynamoDBDocumentManager manager; + private DynamoDBDatabaseManager manager; private DynamoDBTemplate template; @BeforeEach void setup() { - manager = Mockito.mock(DynamoDBDocumentManager.class); + manager = Mockito.mock(DynamoDBDatabaseManager.class); Instance instance = Mockito.mock(Instance.class); when(instance.get()).thenReturn(manager); template = new DefaultDynamoDBTemplate(instance, converter, persistManager, entities, converters); } @Test - public void shouldFindSQL() { + void shouldFindSQL() { template.partiQL("select from database"); Mockito.verify(manager).partiQL("select from database"); } @Test - public void shouldFindSQLWithTypeAndParameters() { + void shouldFindSQLWithTypeAndParameters() { template.partiQL("select from database where content.name = ?", List.of("Ada"), String.class); Mockito.verify(manager).partiQL("select from database where content.name = ?", List.of("Ada"), String.class); } @Test - public void shouldDeleteAll(){ - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(DocumentDeleteQuery.class); + void shouldDeleteAll(){ + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(DeleteQuery.class); template.deleteAll(Person.class); Mockito.verify(manager).delete(argumentCaptor.capture()); - DocumentDeleteQuery query = argumentCaptor.getValue(); + var query = argumentCaptor.getValue(); SoftAssertions.assertSoftly(soft -> { soft.assertThat(query.name()).isEqualTo("Person"); - soft.assertThat(query.documents()).isEmpty(); + soft.assertThat(query.columns()).isEmpty(); soft.assertThat(query.condition()).isEmpty(); }); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtensionTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtensionTest.java index 4c4795076..524f3c696 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtensionTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBExtensionTest.java @@ -16,10 +16,9 @@ package org.eclipse.jnosql.databases.dynamodb.mapping; import jakarta.inject.Inject; -import org.eclipse.jnosql.mapping.Convert; +import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; -import org.eclipse.jnosql.mapping.keyvalue.spi.KeyValueExtension; import org.eclipse.jnosql.mapping.reflection.Reflections; import org.jboss.weld.junit5.auto.AddExtensions; import org.jboss.weld.junit5.auto.AddPackages; @@ -30,7 +29,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @EnableAutoWeld -@AddPackages(value = {Convert.class}) +@AddPackages(value = {Converters.class}) @AddPackages(MockProducer.class) @AddPackages(Reflections.class) @AddExtensions({EntityMetadataExtension.class, diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxyTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxyTest.java index b1b5d5933..4596ebd8e 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxyTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBRepositoryProxyTest.java @@ -20,10 +20,10 @@ import org.assertj.core.api.Assertions; import org.eclipse.jnosql.mapping.core.Converters; import org.eclipse.jnosql.mapping.core.spi.EntityMetadataExtension; -import org.eclipse.jnosql.mapping.document.DocumentEntityConverter; import org.eclipse.jnosql.mapping.document.spi.DocumentExtension; import org.eclipse.jnosql.mapping.metadata.EntitiesMetadata; import org.eclipse.jnosql.mapping.reflection.Reflections; +import org.eclipse.jnosql.mapping.semistructured.EntityConverter; import org.jboss.weld.junit5.auto.AddExtensions; import org.jboss.weld.junit5.auto.AddPackages; import org.jboss.weld.junit5.auto.EnableAutoWeld; @@ -43,7 +43,7 @@ import static org.mockito.Mockito.when; @EnableAutoWeld -@AddPackages(value = {Converters.class, DocumentEntityConverter.class, PartiQL.class}) +@AddPackages(value = {Converters.class, EntityConverter.class, PartiQL.class}) @AddPackages(MockProducer.class) @AddPackages(Reflections.class) @AddExtensions({EntityMetadataExtension.class, DocumentExtension.class, DynamoDBExtension.class}) diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/MockProducer.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/MockProducer.java index 6a9c909da..818521d9b 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/MockProducer.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/mapping/MockProducer.java @@ -19,24 +19,24 @@ import jakarta.enterprise.inject.Alternative; import jakarta.enterprise.inject.Produces; import jakarta.interceptor.Interceptor; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentEntity; -import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDocumentManager; +import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; +import org.eclipse.jnosql.communication.semistructured.Element; +import org.eclipse.jnosql.databases.dynamodb.communication.DynamoDBDatabaseManager; import org.mockito.Mockito; import java.util.function.Supplier; @Alternative @Priority(Interceptor.Priority.APPLICATION) -public class MockProducer implements Supplier { +public class MockProducer implements Supplier { @Produces @Override - public DynamoDBDocumentManager get() { - DynamoDBDocumentManager manager = Mockito.mock(DynamoDBDocumentManager.class); - DocumentEntity entity = DocumentEntity.of("Person"); - entity.add(Document.of("name", "Ada")); - Mockito.when(manager.insert(Mockito.any(DocumentEntity.class))).thenReturn(entity); + public DynamoDBDatabaseManager get() { + DynamoDBDatabaseManager manager = Mockito.mock(DynamoDBDatabaseManager.class); + var entity = CommunicationEntity.of("Person"); + entity.add(Element.of("name", "Ada")); + Mockito.when(manager.insert(Mockito.any(CommunicationEntity.class))).thenReturn(entity); return manager; } From 85704b3a49984ea910bd7d095c6ba07abd2bcc71 Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Tue, 12 Mar 2024 15:36:44 -0300 Subject: [PATCH 70/70] chore: removed unnecessary dependency Signed-off-by: Maximillian Arruda --- jnosql-dynamodb/pom.xml | 4 ---- .../communication/DefaultDynamoDBDatabaseManager.java | 6 ------ .../jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java | 3 +-- .../dynamodb/communication/DynamoDBConverterTest.java | 2 -- 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/jnosql-dynamodb/pom.xml b/jnosql-dynamodb/pom.xml index 2d22e3d7e..0c1214d09 100644 --- a/jnosql-dynamodb/pom.xml +++ b/jnosql-dynamodb/pom.xml @@ -30,10 +30,6 @@ org.eclipse.jnosql.mapping jnosql-mapping-key-value - - org.eclipse.jnosql.communication - jnosql-communication-document - org.eclipse.jnosql.mapping jnosql-mapping-document diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManager.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManager.java index 9886d88fa..518e2fb45 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManager.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/communication/DefaultDynamoDBDatabaseManager.java @@ -39,7 +39,6 @@ import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; import software.amazon.awssdk.services.dynamodb.model.ScanRequest; import software.amazon.awssdk.services.dynamodb.model.Select; -import software.amazon.awssdk.services.dynamodb.model.StreamSpecification; import software.amazon.awssdk.services.dynamodb.model.TimeToLiveStatus; import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; @@ -147,7 +146,6 @@ private DescribeTableResponse createTable(String tableName) { .keySchema(defaultKeySchemaFor()) .attributeDefinitions(defaultAttributeDefinitionsFor()) .provisionedThroughput(defaultProvisionedThroughputFor()) - .streamSpecification(defaultStreamSpecificationFor()) .build()); var tableRequest = DescribeTableRequest.builder().tableName(tableName).build(); @@ -156,10 +154,6 @@ private DescribeTableResponse createTable(String tableName) { } } - private StreamSpecification defaultStreamSpecificationFor() { - return null; - } - private ProvisionedThroughput defaultProvisionedThroughputFor() { return DynamoTableUtils.createProvisionedThroughput(null, null); } diff --git a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java index f766110e6..57bd7c557 100644 --- a/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java +++ b/jnosql-dynamodb/src/main/java/org/eclipse/jnosql/databases/dynamodb/mapping/DynamoDBTemplate.java @@ -15,7 +15,6 @@ package org.eclipse.jnosql.databases.dynamodb.mapping; -import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.mapping.document.DocumentTemplate; import java.util.stream.Stream; @@ -51,7 +50,7 @@ public interface DynamoDBTemplate extends DocumentTemplate { *

Example query: {@code SELECT * FROM users WHERE status = ?}

* * @param query the PartiQL query - * @return a {@link Stream} of {@link DocumentEntity} representing the query result + * @return a {@link Stream} of results representing the query result * @throws NullPointerException when the query is null */ Stream partiQL(String query, Object... params); diff --git a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java index 1b7404cfb..67f3c56ee 100644 --- a/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java +++ b/jnosql-dynamodb/src/test/java/org/eclipse/jnosql/databases/dynamodb/communication/DynamoDBConverterTest.java @@ -18,8 +18,6 @@ import jakarta.json.Json; import jakarta.json.bind.Jsonb; import net.datafaker.Faker; -import org.eclipse.jnosql.communication.document.Document; -import org.eclipse.jnosql.communication.document.DocumentEntity; import org.eclipse.jnosql.communication.driver.JsonbSupplier; import org.eclipse.jnosql.communication.semistructured.CommunicationEntity; import org.eclipse.jnosql.communication.semistructured.Element;