Skip to content

Commit

Permalink
Make it clearer which fields are required when building a DynamoDB En…
Browse files Browse the repository at this point in the history
…hanced Client WriteBatch. Currently, a NullPointerException is thrown if you forget to call the builder with mappedTableResource(MappedTableResource<T>)
  • Loading branch information
Steven Swartz committed Aug 6, 2023
1 parent 21bdc95 commit 2fb4633
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "Amazon DynamoDB Enhanced Client",
"contributor": "swar8080",
"description": "Make it clearer which fields are required when building a WriteBatch, which helps avoid NullPointerExceptions when adding a putItem or deleteItem to the batch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,31 @@ private WriteBatch(BuilderImpl<?> builder) {
this.writeRequests = getItemsFromSupplier(builder.itemSupplierList);
}

/**
* Creates a newly initialized builder for a write batch.
*
* @param itemClass the class that items in this table map to
* @param mappedTableResource the mapped table resource (table) that the items in this write batch should come from
* @param <T> The type of the modelled object to be written in this batch
* @return a WriteBatch builder
*/
public static <T> Builder<T> builder(Class<? extends T> itemClass, MappedTableResource<T> mappedTableResource) {
return new BuilderImpl<>(itemClass, mappedTableResource);
}

/**
* Creates a newly initialized builder for a write batch.
*
* @param itemClass the class that items in this table map to
* @param <T> The type of the modelled object, corresponding to itemClass
* @return a WriteBatch builder
*
* @deprecated Use {@link WriteBatch#builder(Class, MappedTableResource)} to ensure the {@link WriteBatch} is built
* with the required fields.
*/
@Deprecated
public static <T> Builder<T> builder(Class<? extends T> itemClass) {
return new BuilderImpl<>(itemClass);
return builder(itemClass, null);
}

/**
Expand Down Expand Up @@ -192,8 +208,9 @@ private static final class BuilderImpl<T> implements Builder<T> {
private List<Supplier<WriteRequest>> itemSupplierList = new ArrayList<>();
private MappedTableResource<T> mappedTableResource;

private BuilderImpl(Class<? extends T> itemClass) {
private BuilderImpl(Class<? extends T> itemClass, MappedTableResource<T> mappedTableResource) {
this.itemClass = itemClass;
this.mappedTableResource = mappedTableResource;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ public void setupMappedTables() {

@Test
public void builder_minimal() {
WriteBatch builtObject = WriteBatch.builder(FakeItem.class, fakeItemMappedTable).build();

assertThat(builtObject.tableName(), is(TABLE_NAME));
assertThat(builtObject.writeRequests(), is(nullValue()));
}

@Test
public void builder_minimal_deprecated() {
WriteBatch builtObject = WriteBatch.builder(FakeItem.class).build();

assertThat(builtObject.tableName(), is(nullValue()));
Expand All @@ -65,6 +73,22 @@ public void builder_minimal() {
public void builder_maximal_consumer_style() {
FakeItem fakeItem = createUniqueFakeItem();

WriteBatch builtObject = WriteBatch.builder(FakeItem.class, fakeItemMappedTable)
.addPutItem(r -> r.item(fakeItem))
.addDeleteItem(r -> r.key(k -> k.partitionValue(fakeItem.getId())))
.build();

Map<String, AttributeValue> fakeItemMap = FakeItem.getTableSchema().itemToMap(fakeItem,
FakeItem.getTableMetadata().primaryKeys());

assertThat(builtObject.tableName(), is(TABLE_NAME));
assertThat(builtObject.writeRequests(), containsInAnyOrder(putRequest(fakeItemMap), deleteRequest(fakeItemMap)));
}

@Test
public void builder_maximal_consumer_style_deprecated() {
FakeItem fakeItem = createUniqueFakeItem();

WriteBatch builtObject = WriteBatch.builder(FakeItem.class)
.mappedTableResource(fakeItemMappedTable)
.addPutItem(r -> r.item(fakeItem))
Expand All @@ -82,6 +106,27 @@ public void builder_maximal_consumer_style() {
public void builder_maximal_builder_style() {
FakeItem fakeItem = createUniqueFakeItem();

PutItemEnhancedRequest<FakeItem> putItem = PutItemEnhancedRequest.builder(FakeItem.class).item(fakeItem).build();
DeleteItemEnhancedRequest deleteItem = DeleteItemEnhancedRequest.builder()
.key(k -> k.partitionValue(fakeItem.getId()))
.build();

WriteBatch builtObject = WriteBatch.builder(FakeItem.class, fakeItemMappedTable)
.addPutItem(putItem)
.addDeleteItem(deleteItem)
.build();

Map<String, AttributeValue> fakeItemMap = FakeItem.getTableSchema().itemToMap(fakeItem,
FakeItem.getTableMetadata().primaryKeys());

assertThat(builtObject.tableName(), is(TABLE_NAME));
assertThat(builtObject.writeRequests(), containsInAnyOrder(putRequest(fakeItemMap), deleteRequest(fakeItemMap)));
}

@Test
public void builder_maximal_builder_style_deprecated() {
FakeItem fakeItem = createUniqueFakeItem();

PutItemEnhancedRequest<FakeItem> putItem = PutItemEnhancedRequest.builder(FakeItem.class).item(fakeItem).build();
DeleteItemEnhancedRequest deleteItem = DeleteItemEnhancedRequest.builder()
.key(k -> k.partitionValue(fakeItem.getId()))
Expand Down

0 comments on commit 2fb4633

Please sign in to comment.