33import static org .assertj .core .api .Assertions .assertThat ;
44import static org .assertj .core .api .Assertions .assertThatThrownBy ;
55import static software .amazon .awssdk .enhanced .dynamodb .internal .EnhancedClientUtils .keyRef ;
6- import static software .amazon .awssdk .enhanced .dynamodb .internal . EnhancedClientUtils . valueRef ;
6+ import static software .amazon .awssdk .enhanced .dynamodb .mapper . StaticAttributeTags . primaryPartitionKey ;
77
88import java .util .ArrayList ;
99import java .util .Arrays ;
@@ -305,9 +305,20 @@ public void updateExpressionInRequest_whenAttributeAlsoInPojo_shouldThrowConflic
305305 @ Test
306306 public void updateExpressionInRequest_whenAttributeAlsoInExtension_shouldThrowDynamoDbError () {
307307 initClientWithExtensions (new ItemPreservingUpdateExtension ());
308- RecordForUpdateExpressions recordForUpdateExpressions = createFullRecord ();
308+ RecordForUpdateExpressions recordForUpdateExpressions = createKeyOnlyRecord ();
309+
310+ // Create an UpdateExpression that conflicts with the extension's UpdateExpression
311+ // Extension modifies extensionNumberAttribute, so we create a request expression that also modifies it
312+ UpdateExpression conflictingExpression = UpdateExpression .builder ()
313+ .addAction (SetAction .builder ()
314+ .path ("extensionNumberAttribute" )
315+ .value (":conflictValue" )
316+ .putExpressionValue (":conflictValue" , AttributeValue .builder ().n ("99" ).build ())
317+ .build ())
318+ .build ();
319+
309320 assertThatThrownBy (() -> mappedTable .updateItem (r -> r .item (recordForUpdateExpressions )
310- .updateExpression (expressionWithSetExtensionAttribute () )))
321+ .updateExpression (conflictingExpression )))
311322 .isInstanceOf (DynamoDbException .class )
312323 .hasMessageContaining ("Two document paths" )
313324 .hasMessageContaining (NUMBER_ATTRIBUTE_REF );
@@ -560,7 +571,7 @@ public void transactWriteItems_withUpdateExpression() {
560571 .addPutItem (mappedTable , record2 )
561572 .build ());
562573
563- // Verify both operations succeeded
574+ // Verify both operations succeeded
564575 RecordForUpdateExpressions deletedRecord = mappedTable .getItem (record1 );
565576 assertThat (deletedRecord ).isNull ();
566577
@@ -569,6 +580,51 @@ public void transactWriteItems_withUpdateExpression() {
569580 assertThat (persistedRecord2 .getRequestAttributeList ()).containsExactly ("attr1" , "attr2" );
570581 }
571582
583+ /**
584+ * Tests StaticTableSchema with UpdateExpression extensions
585+ */
586+ @ Test
587+ public void staticTableSchema_withUpdateExpressions () {
588+ TableSchema <RecordForUpdateExpressions > staticSchema = TableSchema .builder (RecordForUpdateExpressions .class )
589+ .newItemSupplier (RecordForUpdateExpressions ::new )
590+ .addAttribute (String .class , a -> a .name ("id" )
591+ .getter (RecordForUpdateExpressions ::getId )
592+ .setter (RecordForUpdateExpressions ::setId )
593+ .tags (primaryPartitionKey ()))
594+ .addAttribute (String .class , a -> a .name ("stringAttribute" )
595+ .getter (RecordForUpdateExpressions ::getStringAttribute )
596+ .setter (RecordForUpdateExpressions ::setStringAttribute ))
597+ .addAttribute (Long .class , a -> a .name ("extensionNumberAttribute" )
598+ .getter (RecordForUpdateExpressions ::getExtensionNumberAttribute )
599+ .setter (RecordForUpdateExpressions ::setExtensionNumberAttribute ))
600+ .build ();
601+
602+ DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient .builder ()
603+ .dynamoDbClient (getDynamoDbClient ())
604+ .extensions (new ItemPreservingUpdateExtension ())
605+ .build ();
606+
607+ String staticTableName = getConcreteTableName ("static-table" );
608+ DynamoDbTable <RecordForUpdateExpressions > staticTable = enhancedClient .table (staticTableName , staticSchema );
609+
610+ try {
611+ staticTable .createTable (r -> r .provisionedThroughput (getDefaultProvisionedThroughput ()));
612+
613+ RecordForUpdateExpressions record = new RecordForUpdateExpressions ();
614+ record .setId ("static-test" );
615+ record .setStringAttribute ("init" );
616+ // Don't set requestAttributeList to avoid path conflicts with extension
617+
618+ staticTable .updateItem (r -> r .item (record ));
619+
620+ RecordForUpdateExpressions persistedRecord = staticTable .getItem (record );
621+ assertThat (persistedRecord .getStringAttribute ()).isEqualTo ("init" );
622+ assertThat (persistedRecord .getExtensionNumberAttribute ()).isEqualTo (5L );
623+ } finally {
624+ getDynamoDbClient ().deleteTable (r -> r .tableName (staticTableName ));
625+ }
626+ }
627+
572628 private void verifyDDBError (RecordForUpdateExpressions record , boolean ignoreNulls ) {
573629 assertThatThrownBy (() -> mappedTable .updateItem (r -> r .item (record ).ignoreNulls (ignoreNulls )))
574630 .isInstanceOf (DynamoDbException .class )
@@ -628,18 +684,6 @@ private UpdateExpression expressionWithSetListElement(int index, String value) {
628684 return UpdateExpression .builder ().addAction (setListElement ).build ();
629685 }
630686
631- private UpdateExpression expressionWithSetExtensionAttribute () {
632- String attributeName = "extensionNumberAttribute" ;
633- AttributeValue elementValue = AttributeValue .builder ().n ("11" ).build ();
634- SetAction setAttribute = SetAction .builder ()
635- .path (keyRef (attributeName ))
636- .value (valueRef (attributeName ))
637- .putExpressionValue (valueRef (attributeName ), elementValue )
638- .putExpressionName (keyRef (attributeName ), attributeName )
639- .build ();
640- return UpdateExpression .builder ().addAction (setAttribute ).build ();
641- }
642-
643687 private static final class ItemPreservingUpdateExtension implements DynamoDbEnhancedClientExtension {
644688 private long incrementValue ;
645689 private String valueRef ;
0 commit comments