From d2bb0568a6c3be3513bb31e67e5ce9d32aaf0a2e Mon Sep 17 00:00:00 2001 From: Maximillian Arruda Date: Sun, 7 Jan 2024 19:46:47 -0300 Subject: [PATCH] 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); + }); + + } + } }