Skip to content

Commit 250c65d

Browse files
authored
fix: Fix squashing of empty exclusiveStartKey (#218)
1 parent fa78f23 commit 250c65d

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

DynamoDbEncryption/runtimes/java/src/main/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/DynamoDbEncryptionInterceptor.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,40 @@ public SdkRequest modifyRequest(Context.ModifyRequest context, ExecutionAttribut
125125
outgoingRequest = copyOverrideConfig((PutItemRequest) originalRequest, transformedRequest);
126126
break;
127127
} case "Query": {
128+
QueryRequest queryRequest = (QueryRequest) originalRequest;
128129
QueryRequest transformedRequest = transformer.QueryInputTransform(
129130
QueryInputTransformInput.builder()
130-
.sdkInput((QueryRequest) originalRequest)
131+
.sdkInput(queryRequest)
131132
.build()).transformedInput();
132-
outgoingRequest = copyOverrideConfig((QueryRequest) originalRequest, transformedRequest);
133+
134+
// Our current Java->Dafny conversion squashes empty maps into the "None" type.
135+
// In order to avoid gray failures for invalid `exclusiveStartKey`,
136+
// and because our transforms do not act on or modify this value currently,
137+
// copy over the original `exclusiveStartKey`
138+
// so that the server can correctly reject it as invalid if it is empty.
139+
transformedRequest = transformedRequest.toBuilder()
140+
.exclusiveStartKey(queryRequest.exclusiveStartKey())
141+
.build();
142+
143+
outgoingRequest = copyOverrideConfig(queryRequest, transformedRequest);
133144
break;
134145
} case "Scan": {
146+
ScanRequest scanRequest = (ScanRequest) originalRequest;
135147
ScanRequest transformedRequest = transformer.ScanInputTransform(
136148
ScanInputTransformInput.builder()
137-
.sdkInput((ScanRequest) originalRequest)
149+
.sdkInput(scanRequest)
138150
.build()).transformedInput();
139-
outgoingRequest = copyOverrideConfig((ScanRequest) originalRequest, transformedRequest);
151+
152+
// Our current Java->Dafny conversion squashes empty maps into the "None" type.
153+
// In order to avoid gray failures for invalid `exclusiveStartKey`,
154+
// and because our transforms do not act on or modify this value currently,
155+
// copy over the original `exclusiveStartKey`
156+
// so that the server can correctly reject it as invalid if it is empty.
157+
transformedRequest = transformedRequest.toBuilder()
158+
.exclusiveStartKey(scanRequest.exclusiveStartKey())
159+
.build();
160+
161+
outgoingRequest = copyOverrideConfig(scanRequest, transformedRequest);
140162
break;
141163
} case "TransactGetItems": {
142164
TransactGetItemsRequest transformedRequest = transformer.TransactGetItemsInputTransform(

DynamoDbEncryption/runtimes/java/src/test/java/software/amazon/cryptography/dbencryptionsdk/dynamodb/DynamoDbEncryptionInterceptorTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import software.amazon.awssdk.core.ClientType;
55
import software.amazon.awssdk.core.SdkRequest;
66
import software.amazon.awssdk.core.interceptor.*;
7+
import software.amazon.awssdk.core.util.DefaultSdkAutoConstructMap;
78
import software.amazon.awssdk.services.dynamodb.model.*;
89

910
import java.util.*;
@@ -550,4 +551,46 @@ public void TestPutMissingSort() {
550551

551552
interceptor.modifyRequest(context, attributes);
552553
}
554+
555+
@Test
556+
public void TestQueryPreservesEmptyExclusiveStartKey() {
557+
QueryRequest oldRequest = QueryRequest.builder()
558+
.tableName(TEST_TABLE_NAME)
559+
.exclusiveStartKey(new HashMap<>())
560+
.build();
561+
562+
Context.ModifyRequest context = InterceptorContext.builder()
563+
.request(oldRequest)
564+
.build();
565+
566+
ExecutionAttributes attributes = validAttributes.toBuilder()
567+
.put(SdkExecutionAttribute.OPERATION_NAME, "Query")
568+
.build();
569+
570+
SdkRequest newRequest = interceptor.modifyRequest(context, attributes);
571+
assertTrue(newRequest instanceof QueryRequest);
572+
assertFalse(((QueryRequest) newRequest).exclusiveStartKey() instanceof DefaultSdkAutoConstructMap);
573+
assertEquals(((QueryRequest) newRequest).exclusiveStartKey().size(), 0);
574+
}
575+
576+
@Test
577+
public void TestScanPreservesEmptyExclusiveStartKey() {
578+
ScanRequest oldRequest = ScanRequest.builder()
579+
.tableName(TEST_TABLE_NAME)
580+
.exclusiveStartKey(new HashMap<>())
581+
.build();
582+
583+
Context.ModifyRequest context = InterceptorContext.builder()
584+
.request(oldRequest)
585+
.build();
586+
587+
ExecutionAttributes attributes = validAttributes.toBuilder()
588+
.put(SdkExecutionAttribute.OPERATION_NAME, "Scan")
589+
.build();
590+
591+
SdkRequest newRequest = interceptor.modifyRequest(context, attributes);
592+
assertTrue(newRequest instanceof ScanRequest);
593+
assertFalse(((ScanRequest) newRequest).exclusiveStartKey() instanceof DefaultSdkAutoConstructMap);
594+
assertEquals(((ScanRequest) newRequest).exclusiveStartKey().size(), 0);
595+
}
553596
}

0 commit comments

Comments
 (0)