Skip to content

Commit 88213ab

Browse files
authored
chore(client-sqs): create coercing serializers for awsQueryCompat (#5440)
* chore(client-sqs): use query-compat model * chore(client-sqs): generate as JSON protocol * chore(client-sqs): create coercing serializers for awsQueryCompat * chore(client-sqs): restore default model * chore(client-sqs): restore default client sqs
1 parent 4c7fe9c commit 88213ab

File tree

6 files changed

+250
-7
lines changed

6 files changed

+250
-7
lines changed

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/JsonMemberSerVisitor.java

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,21 @@
1515

1616
package software.amazon.smithy.aws.typescript.codegen;
1717

18+
import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait;
1819
import software.amazon.smithy.codegen.core.CodegenException;
1920
import software.amazon.smithy.model.shapes.BigDecimalShape;
2021
import software.amazon.smithy.model.shapes.BigIntegerShape;
22+
import software.amazon.smithy.model.shapes.BooleanShape;
23+
import software.amazon.smithy.model.shapes.DoubleShape;
24+
import software.amazon.smithy.model.shapes.FloatShape;
25+
import software.amazon.smithy.model.shapes.IntegerShape;
26+
import software.amazon.smithy.model.shapes.LongShape;
2127
import software.amazon.smithy.model.shapes.Shape;
28+
import software.amazon.smithy.model.shapes.ShortShape;
29+
import software.amazon.smithy.model.shapes.StringShape;
2230
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
2331
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
32+
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
2433
import software.amazon.smithy.typescript.codegen.integration.DocumentMemberSerVisitor;
2534
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;
2635
import software.amazon.smithy.utils.SmithyInternalApi;
@@ -33,14 +42,22 @@
3342
*/
3443
@SmithyInternalApi
3544
final class JsonMemberSerVisitor extends DocumentMemberSerVisitor {
45+
private final boolean isAwsQueryCompat;
3646

3747
/**
3848
* @inheritDoc
3949
*/
4050
JsonMemberSerVisitor(GenerationContext context, String dataSource, Format defaultTimestampFormat) {
4151
super(context, dataSource, defaultTimestampFormat);
42-
context.getWriter().addImport("_json", null, TypeScriptDependency.AWS_SMITHY_CLIENT);
43-
this.serdeElisionEnabled = !context.getSettings().generateServerSdk();
52+
TypeScriptWriter writer = context.getWriter();
53+
writer.addImport("_json", null, TypeScriptDependency.AWS_SMITHY_CLIENT);
54+
this.isAwsQueryCompat = context.getService().hasTrait(AwsQueryCompatibleTrait.class);
55+
this.serdeElisionEnabled = !this.isAwsQueryCompat && !context.getSettings().generateServerSdk();
56+
if (isAwsQueryCompat) {
57+
writer.addImport("_toStr", null, AwsDependency.AWS_SDK_CORE);
58+
writer.addImport("_toNum", null, AwsDependency.AWS_SDK_CORE);
59+
writer.addImport("_toBool", null, AwsDependency.AWS_SDK_CORE);
60+
}
4461
}
4562

4663
@Override
@@ -55,6 +72,69 @@ public String bigIntegerShape(BigIntegerShape shape) {
5572
return unsupportedShape(shape);
5673
}
5774

75+
@Override
76+
public String shortShape(ShortShape shape) {
77+
String base = super.shortShape(shape);
78+
if (isAwsQueryCompat) {
79+
return "_toNum(" + base + ")";
80+
}
81+
return base;
82+
}
83+
84+
@Override
85+
public String integerShape(IntegerShape shape) {
86+
String base = super.integerShape(shape);
87+
if (isAwsQueryCompat) {
88+
return "_toNum(" + base + ")";
89+
}
90+
return base;
91+
}
92+
93+
@Override
94+
public String longShape(LongShape shape) {
95+
String base = super.longShape(shape);
96+
if (isAwsQueryCompat) {
97+
return "_toNum(" + base + ")";
98+
}
99+
return base;
100+
}
101+
102+
@Override
103+
public String floatShape(FloatShape shape) {
104+
String base = super.floatShape(shape);
105+
if (isAwsQueryCompat) {
106+
return "_toNum(" + base + ")";
107+
}
108+
return base;
109+
}
110+
111+
@Override
112+
public String doubleShape(DoubleShape shape) {
113+
String base = super.doubleShape(shape);
114+
if (isAwsQueryCompat) {
115+
return "_toNum(" + base + ")";
116+
}
117+
return base;
118+
}
119+
120+
@Override
121+
public String booleanShape(BooleanShape shape) {
122+
String base = super.booleanShape(shape);
123+
if (isAwsQueryCompat) {
124+
return "_toBool(" + base + ")";
125+
}
126+
return base;
127+
}
128+
129+
@Override
130+
public String stringShape(StringShape shape) {
131+
String base = super.stringShape(shape);
132+
if (isAwsQueryCompat) {
133+
return "_toStr(" + base + ")";
134+
}
135+
return base;
136+
}
137+
58138
private String unsupportedShape(Shape shape) {
59139
throw new CodegenException(String.format("Cannot serialize shape type %s on protocol, shape: %s.",
60140
shape.getType(), shape.getId()));

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/JsonRpcProtocolGenerator.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,28 @@ protected Format getDocumentTimestampFormat() {
6666

6767
@Override
6868
protected void generateDocumentBodyShapeSerializers(GenerationContext context, Set<Shape> shapes) {
69+
boolean isAwsQueryCompat = context.getService().hasTrait(AwsQueryCompatibleTrait.class);
70+
6971
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes,
70-
// AWS JSON does not support jsonName
71-
new JsonShapeSerVisitor(context, (shape, name) -> name, enableSerdeElision()));
72+
// AWS JSON does not support jsonName
73+
new JsonShapeSerVisitor(
74+
context,
75+
(shape, name) -> name,
76+
!isAwsQueryCompat && enableSerdeElision()
77+
)
78+
);
7279
}
7380

7481
@Override
7582
protected void generateDocumentBodyShapeDeserializers(GenerationContext context, Set<Shape> shapes) {
7683
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes,
77-
// AWS JSON does not support jsonName
78-
new JsonShapeDeserVisitor(context, (shape, name) -> name, enableSerdeElision()));
84+
// AWS JSON does not support jsonName
85+
new JsonShapeDeserVisitor(
86+
context,
87+
(shape, name) -> name,
88+
enableSerdeElision()
89+
)
90+
);
7991
}
8092

8193
@Override

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/RestJsonProtocolGenerator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.List;
1919
import java.util.Set;
20+
import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait;
2021
import software.amazon.smithy.aws.typescript.codegen.validation.UnaryFunctionCall;
2122
import software.amazon.smithy.codegen.core.SymbolProvider;
2223
import software.amazon.smithy.model.knowledge.HttpBinding;
@@ -69,8 +70,9 @@ protected TimestampFormatTrait.Format getDocumentTimestampFormat() {
6970

7071
@Override
7172
protected void generateDocumentBodyShapeSerializers(GenerationContext context, Set<Shape> shapes) {
73+
boolean isAwsQueryCompat = context.getService().hasTrait(AwsQueryCompatibleTrait.class);
7274
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes, new JsonShapeSerVisitor(context,
73-
(!context.getSettings().generateServerSdk() && enableSerdeElision())));
75+
(!context.getSettings().generateServerSdk() && !isAwsQueryCompat && enableSerdeElision())));
7476
}
7577

7678
@Override
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { _toBool, _toNum, _toStr } from "./coercing-serializers";
2+
3+
const consoleWarn = console.warn;
4+
5+
beforeAll(() => {
6+
console.warn = () => {};
7+
});
8+
9+
afterAll(() => {
10+
console.warn = consoleWarn;
11+
});
12+
13+
describe(_toBool.name, () => {
14+
it("ignores nullish", () => {
15+
expect(_toBool(null)).toBe(null);
16+
expect(_toBool(undefined)).toBe(undefined);
17+
});
18+
19+
it("converts strings", () => {
20+
expect(_toBool("false")).toEqual(false);
21+
expect(_toBool("true")).toEqual(true);
22+
23+
expect(_toBool("False")).toEqual(false);
24+
expect(_toBool("True")).toEqual(true);
25+
26+
expect(_toBool("")).toEqual(false);
27+
expect(_toBool("a")).toEqual(true); // warns
28+
});
29+
30+
it("does not convert numbers", () => {
31+
expect(_toBool(0)).toEqual(0);
32+
expect(_toBool(1)).toEqual(1);
33+
});
34+
});
35+
36+
describe(_toStr.name, () => {
37+
it("ignores nullish", () => {
38+
expect(_toStr(null)).toBe(null);
39+
expect(_toStr(undefined)).toBe(undefined);
40+
});
41+
42+
it("converts numbers", () => {
43+
expect(_toStr(0)).toEqual("0");
44+
expect(_toStr(1)).toEqual("1");
45+
});
46+
47+
it("converts booleans", () => {
48+
expect(_toStr(false)).toEqual("false");
49+
expect(_toStr(true)).toEqual("true");
50+
});
51+
});
52+
53+
describe(_toNum.name, () => {
54+
it("ignores nullish", () => {
55+
expect(_toNum(null)).toBe(null);
56+
expect(_toNum(undefined)).toBe(undefined);
57+
});
58+
59+
it("converts numeric strings", () => {
60+
expect(_toNum("1234")).toEqual(1234);
61+
expect(_toNum("1234.56")).toEqual(1234.56);
62+
});
63+
64+
it("does not convert prefix-numeric strings", () => {
65+
expect(_toNum("1234abc")).toEqual("1234abc");
66+
expect(_toNum("1234.56abc")).toEqual("1234.56abc");
67+
});
68+
69+
it("does not convert non-numeric strings", () => {
70+
expect(_toNum("abcdef")).toEqual("abcdef");
71+
});
72+
it("does not convert bools", () => {
73+
expect(_toNum(false)).toEqual(false);
74+
expect(_toNum(true)).toEqual(true);
75+
});
76+
});
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* @internal
3+
*
4+
* Used for awsQueryCompatibility trait.
5+
*/
6+
export const _toStr = (val: unknown): string | undefined => {
7+
if (val == null) {
8+
return val as undefined;
9+
}
10+
if (typeof val === "number" || typeof val === "bigint") {
11+
const warning = new Error(`Received number ${val} where a string was expected.`);
12+
warning.name = "Warning";
13+
console.warn(warning);
14+
return String(val);
15+
}
16+
if (typeof val === "boolean") {
17+
const warning = new Error(`Received boolean ${val} where a string was expected.`);
18+
warning.name = "Warning";
19+
console.warn(warning);
20+
return String(val);
21+
}
22+
return val as string;
23+
};
24+
25+
/**
26+
* @internal
27+
*
28+
* Used for awsQueryCompatibility trait.
29+
*/
30+
export const _toBool = (val: unknown): boolean | undefined => {
31+
if (val == null) {
32+
return val as undefined;
33+
}
34+
if (typeof val === "number") {
35+
// transmit to service to be rejected.
36+
}
37+
if (typeof val === "string") {
38+
const lowercase = val.toLowerCase();
39+
if (val !== "" && lowercase !== "false" && lowercase !== "true") {
40+
const warning = new Error(`Received string "${val}" where a boolean was expected.`);
41+
warning.name = "Warning";
42+
console.warn(warning);
43+
}
44+
return val !== "" && lowercase !== "false";
45+
}
46+
return val as boolean;
47+
};
48+
49+
/**
50+
* @internal
51+
*
52+
* Used for awsQueryCompatibility trait.
53+
*/
54+
export const _toNum = (val: unknown): number | undefined => {
55+
if (val == null) {
56+
return val as undefined;
57+
}
58+
if (typeof val === "boolean") {
59+
// transmit to service to be rejected.
60+
}
61+
if (typeof val === "string") {
62+
const num = Number(val);
63+
if (num.toString() !== val) {
64+
const warning = new Error(`Received string "${val}" where a number was expected.`);
65+
warning.name = "Warning";
66+
console.warn(warning);
67+
return val as unknown as undefined;
68+
}
69+
return num;
70+
}
71+
return val as number;
72+
};

packages/core/src/protocols/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from "./coercing-serializers";
12
export * from "./json/awsExpectUnion";

0 commit comments

Comments
 (0)