Skip to content

Commit dcf644f

Browse files
feat(codegen): reject null in non-sparse collections (#2771)
This updates list parsing for SSDKs to throw an error whenever a null value shows up in a list or set that isn't meant to be sparse. Clients remain tolerant of these nulls to preserve compatibility with services that are not so strict.
1 parent aa62fc3 commit dcf644f

File tree

2 files changed

+12
-6
lines changed

2 files changed

+12
-6
lines changed

Diff for: codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsProtocolUtils.java

-4
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,6 @@ private static boolean filterMalformedRequestTests(
330330
if (testCase.getId().startsWith("RestJsonMalformedUnion")) {
331331
return true;
332332
}
333-
//TODO: we don't do any list validation
334-
if (testCase.getId().startsWith("RestJsonBodyMalformedList")) {
335-
return true;
336-
}
337333
//TODO: we don't validate map values
338334
if (testCase.getId().equals("RestJsonBodyMalformedMapNullValue")) {
339335
return true;

Diff for: codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/JsonShapeDeserVisitor.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import software.amazon.smithy.model.traits.SparseTrait;
3333
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
3434
import software.amazon.smithy.typescript.codegen.CodegenUtils;
35+
import software.amazon.smithy.typescript.codegen.TypeScriptSettings.ArtifactType;
3536
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
3637
import software.amazon.smithy.typescript.codegen.integration.DocumentMemberDeserVisitor;
3738
import software.amazon.smithy.typescript.codegen.integration.DocumentShapeDeserVisitor;
@@ -62,10 +63,11 @@ private DocumentMemberDeserVisitor getMemberVisitor(MemberShape memberShape, Str
6263
protected void deserializeCollection(GenerationContext context, CollectionShape shape) {
6364
TypeScriptWriter writer = context.getWriter();
6465
Shape target = context.getModel().expectShape(shape.getMember().getTarget());
66+
ArtifactType artifactType = context.getSettings().getArtifactType();
6567

6668
// Filter out null entries if we don't have the sparse trait.
6769
String potentialFilter = "";
68-
if (!shape.hasTrait(SparseTrait.ID)) {
70+
if (!shape.hasTrait(SparseTrait.ID) && !artifactType.equals(ArtifactType.SSDK)) {
6971
potentialFilter = ".filter((e: any) => e != null)";
7072
}
7173

@@ -75,7 +77,15 @@ protected void deserializeCollection(GenerationContext context, CollectionShape
7577

7678
writer.openBlock("return (output || [])$L.map((entry: any) => {", "});", potentialFilter, () -> {
7779
// Short circuit null values from serialization.
78-
writer.write("if (entry === null) { return null as any; }");
80+
writer.openBlock("if (entry === null) {", "}", () -> {
81+
// In the SSDK we want to be very strict about not accepting nulls in non-sparse lists.
82+
if (!shape.hasTrait(SparseTrait.ID) && artifactType.equals(ArtifactType.SSDK)) {
83+
writer.write("throw new TypeError('All elements of the non-sparse list $S must be non-null.');",
84+
shape.getId());
85+
} else {
86+
writer.write("return null as any;");
87+
}
88+
});
7989

8090
if (shape.isSetShape()) {
8191
writer.write("const parsedEntry = $L$L;",

0 commit comments

Comments
 (0)