Skip to content

Introduce the possibility to specify returnValuesOnConditionCheckFailure in DynamoDB Enhanced operations #4708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"category": "Amazon DynamoDB Enhanced",
"contributor": "breader124",
"type": "feature",
"description": "Introduce the possibility to specify returnValuesOnConditionCheckFailure in DynamoDB Enhanced operations"
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
package software.amazon.awssdk.enhanced.dynamodb;

import static org.assertj.core.api.Assertions.assertThat;
import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.secondaryPartitionKey;
import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.secondarySortKey;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import org.assertj.core.data.Offset;
import java.util.concurrent.CompletionException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import software.amazon.awssdk.enhanced.dynamodb.model.DeleteItemEnhancedRequest;
import software.amazon.awssdk.enhanced.dynamodb.model.DeleteItemEnhancedResponse;
import software.amazon.awssdk.enhanced.dynamodb.model.EnhancedLocalSecondaryIndex;
import software.amazon.awssdk.enhanced.dynamodb.model.GetItemEnhancedResponse;
Expand All @@ -32,18 +33,17 @@
import software.amazon.awssdk.enhanced.dynamodb.model.UpdateItemEnhancedRequest;
import software.amazon.awssdk.enhanced.dynamodb.model.UpdateItemEnhancedResponse;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.model.ConsumedCapacity;
import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException;
import software.amazon.awssdk.services.dynamodb.model.Projection;
import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;
import software.amazon.awssdk.services.dynamodb.model.ReturnItemCollectionMetrics;
import software.amazon.awssdk.services.dynamodb.model.ReturnValuesOnConditionCheckFailure;

public class AsyncCrudWithResponseIntegrationTest extends DynamoDbEnhancedIntegrationTestBase {


private static final String TABLE_NAME = createTestTableName();


private static final EnhancedLocalSecondaryIndex LOCAL_SECONDARY_INDEX = EnhancedLocalSecondaryIndex.builder()
.indexName("index1")
.projection(Projection.builder()
Expand All @@ -56,16 +56,24 @@ public class AsyncCrudWithResponseIntegrationTest extends DynamoDbEnhancedIntegr
private static DynamoDbAsyncTable<Record> mappedTable;

@BeforeClass
public static void setup() {
public static void beforeClass() {
dynamoDbClient = createAsyncDynamoDbClient();
enhancedClient = DynamoDbEnhancedAsyncClient.builder().dynamoDbClient(dynamoDbClient).build();
mappedTable = enhancedClient.table(TABLE_NAME, TABLE_SCHEMA);
mappedTable.createTable(r -> r.localSecondaryIndices(LOCAL_SECONDARY_INDEX)).join();
dynamoDbClient.waiter().waitUntilTableExists(r -> r.tableName(TABLE_NAME)).join();
}

@After
public void tearDown() {
mappedTable.scan()
.items()
.subscribe(record -> mappedTable.deleteItem(record).join())
.join();
}

@AfterClass
public static void teardown() {
public static void afterClass() {
try {
dynamoDbClient.deleteTable(r -> r.tableName(TABLE_NAME)).join();
} finally {
Expand Down Expand Up @@ -125,33 +133,149 @@ public void updateItem_returnItemCollectionMetrics_set_itemCollectionMetricsNotN
}

@Test
public void deleteItem_returnConsumedCapacity_unset_consumedCapacityNull() {
public void deleteItem_returnItemCollectionMetrics_set_itemCollectionMetricsNull() {
Key key = Key.builder().partitionValue("1").sortValue(10).build();

DeleteItemEnhancedResponse<Record> response = mappedTable.deleteItemWithResponse(r -> r.key(key)).join();

assertThat(response.consumedCapacity()).isNull();
assertThat(response.itemCollectionMetrics()).isNull();
}

@Test
public void deleteItem_returnConsumedCapacity_set_consumedCapacityNotNull() {
public void deleteItem_returnItemCollectionMetrics_set_itemCollectionMetricsNotNull() {
Key key = Key.builder().partitionValue("1").sortValue(10).build();

DeleteItemEnhancedResponse<Record> response =
mappedTable.deleteItemWithResponse(r -> r.key(key).returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)).join();
mappedTable.deleteItemWithResponse(r -> r.key(key).returnItemCollectionMetrics(ReturnItemCollectionMetrics.SIZE))
.join();

assertThat(response.consumedCapacity()).isNotNull();
assertThat(response.itemCollectionMetrics()).isNotNull();
}

@Test
public void putItem_returnValuesOnConditionCheckFailure_set_returnValuesOnConditionCheckFailureNull() {
Record record = new Record().setId("1").setSort(10);
mappedTable.putItem(record).join();

Expression itemDoesNotExist = Expression.builder().expression("attribute_not_exists(id)").build();
PutItemEnhancedRequest<Record> request = PutItemEnhancedRequest.builder(Record.class)
.item(record)
.conditionExpression(itemDoesNotExist)
.build();

assertThatThrownBy(() -> mappedTable.putItem(request).join())
.isInstanceOf(CompletionException.class)
.satisfies(e -> assertThat(((ConditionalCheckFailedException) e.getCause()).hasItem()).isFalse());
}

@Test
public void putItem_returnValuesOnConditionCheckFailure_set_returnValuesOnConditionCheckFailureNotNull() {
Record record = new Record().setId("1").setSort(10);
mappedTable.putItem(record).join();

Expression itemDoesNotExist = Expression.builder().expression("attribute_not_exists(id)").build();
PutItemEnhancedRequest<Record> request = PutItemEnhancedRequest.builder(Record.class)
.item(record)
.conditionExpression(itemDoesNotExist)
.returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
.build();

assertThatThrownBy(() -> mappedTable.putItem(request).join())
.isInstanceOf(CompletionException.class)
.satisfies(e -> assertThat(((ConditionalCheckFailedException) e.getCause()).hasItem()).isTrue());
}

@Test
public void updateItem_returnValuesOnConditionCheckFailure_set_returnValuesOnConditionCheckFailureNull() {
Record record = new Record().setId("1").setSort(10);
mappedTable.putItem(record).join();

Expression itemDoesNotExist = Expression.builder().expression("attribute_not_exists(id)").build();
UpdateItemEnhancedRequest<Record> request = UpdateItemEnhancedRequest.builder(Record.class)
.item(record)
.conditionExpression(itemDoesNotExist)
.build();

assertThatThrownBy(() -> mappedTable.updateItem(request).join())
.isInstanceOf(CompletionException.class)
.satisfies(e -> assertThat(((ConditionalCheckFailedException) e.getCause()).hasItem()).isFalse());
}

@Test
public void updateItem_returnValuesOnConditionCheckFailure_set_returnValuesOnConditionCheckFailureNotNull() {
Record record = new Record().setId("1").setSort(10);
mappedTable.putItem(record).join();

Expression itemDoesNotExist = Expression.builder().expression("attribute_not_exists(id)").build();
UpdateItemEnhancedRequest<Record> request = UpdateItemEnhancedRequest.builder(Record.class)
.item(record)
.conditionExpression(itemDoesNotExist)
.returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
.build();

assertThatThrownBy(() -> mappedTable.updateItem(request).join())
.isInstanceOf(CompletionException.class)
.satisfies(e -> assertThat(((ConditionalCheckFailedException) e.getCause()).hasItem()).isTrue());
}

@Test
public void deleteItem_returnValuesOnConditionCheckFailure_set_returnValuesOnConditionCheckFailureNull() {
Record record = new Record().setId("1").setSort(10);
Key recordKey = Key.builder()
.partitionValue(record.getId())
.sortValue(record.getSort())
.build();
mappedTable.putItem(record).join();

Expression itemDoesNotExist = Expression.builder().expression("attribute_not_exists(id)").build();
DeleteItemEnhancedRequest request = DeleteItemEnhancedRequest.builder()
.key(recordKey)
.conditionExpression(itemDoesNotExist)
.build();

assertThatThrownBy(() -> mappedTable.deleteItem(request).join())
.isInstanceOf(CompletionException.class)
.satisfies(e -> assertThat(((ConditionalCheckFailedException) e.getCause()).hasItem()).isFalse());
}

@Test
public void deleteItem_returnValuesOnConditionCheckFailure_set_returnValuesOnConditionCheckFailureNotNull() {
Record record = new Record().setId("1").setSort(10);
Key recordKey = Key.builder()
.partitionValue(record.getId())
.sortValue(record.getSort())
.build();
mappedTable.putItem(record).join();

Expression itemDoesNotExist = Expression.builder().expression("attribute_not_exists(id)").build();
DeleteItemEnhancedRequest request = DeleteItemEnhancedRequest.builder()
.key(recordKey)
.conditionExpression(itemDoesNotExist)
.returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
.build();

assertThatThrownBy(() -> mappedTable.deleteItem(request).join())
.isInstanceOf(CompletionException.class)
.satisfies(e -> assertThat(((ConditionalCheckFailedException) e.getCause()).hasItem()).isTrue());
}

@Test
public void deleteItem_returnConsumedCapacity_unset_consumedCapacityNull() {
Key key = Key.builder().partitionValue("1").sortValue(10).build();

DeleteItemEnhancedResponse<Record> response = mappedTable.deleteItemWithResponse(r -> r.key(key)).join();

assertThat(response.consumedCapacity()).isNull();
}

@Test
public void delete_returnItemCollectionMetrics_set_itemCollectionMetricsNotNull() {
public void deleteItem_returnConsumedCapacity_set_consumedCapacityNotNull() {
Key key = Key.builder().partitionValue("1").sortValue(10).build();

DeleteItemEnhancedResponse<Record> response =
mappedTable.deleteItemWithResponse(r -> r.key(key).returnItemCollectionMetrics(ReturnItemCollectionMetrics.SIZE))
.join();
mappedTable.deleteItemWithResponse(r -> r.key(key).returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)).join();

assertThat(response.itemCollectionMetrics()).isNotNull();
assertThat(response.consumedCapacity()).isNotNull();
}

@Test
Expand Down
Loading