Skip to content

Commit

Permalink
chore(client-sqs): create coercing serializers for awsQueryCompat (#5440
Browse files Browse the repository at this point in the history
)

* 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
  • Loading branch information
kuhe authored Oct 31, 2023
1 parent 4c7fe9c commit 88213ab
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,21 @@

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

import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait;
import software.amazon.smithy.codegen.core.CodegenException;
import software.amazon.smithy.model.shapes.BigDecimalShape;
import software.amazon.smithy.model.shapes.BigIntegerShape;
import software.amazon.smithy.model.shapes.BooleanShape;
import software.amazon.smithy.model.shapes.DoubleShape;
import software.amazon.smithy.model.shapes.FloatShape;
import software.amazon.smithy.model.shapes.IntegerShape;
import software.amazon.smithy.model.shapes.LongShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShortShape;
import software.amazon.smithy.model.shapes.StringShape;
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
import software.amazon.smithy.typescript.codegen.TypeScriptDependency;
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
import software.amazon.smithy.typescript.codegen.integration.DocumentMemberSerVisitor;
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;
import software.amazon.smithy.utils.SmithyInternalApi;
Expand All @@ -33,14 +42,22 @@
*/
@SmithyInternalApi
final class JsonMemberSerVisitor extends DocumentMemberSerVisitor {
private final boolean isAwsQueryCompat;

/**
* @inheritDoc
*/
JsonMemberSerVisitor(GenerationContext context, String dataSource, Format defaultTimestampFormat) {
super(context, dataSource, defaultTimestampFormat);
context.getWriter().addImport("_json", null, TypeScriptDependency.AWS_SMITHY_CLIENT);
this.serdeElisionEnabled = !context.getSettings().generateServerSdk();
TypeScriptWriter writer = context.getWriter();
writer.addImport("_json", null, TypeScriptDependency.AWS_SMITHY_CLIENT);
this.isAwsQueryCompat = context.getService().hasTrait(AwsQueryCompatibleTrait.class);
this.serdeElisionEnabled = !this.isAwsQueryCompat && !context.getSettings().generateServerSdk();
if (isAwsQueryCompat) {
writer.addImport("_toStr", null, AwsDependency.AWS_SDK_CORE);
writer.addImport("_toNum", null, AwsDependency.AWS_SDK_CORE);
writer.addImport("_toBool", null, AwsDependency.AWS_SDK_CORE);
}
}

@Override
Expand All @@ -55,6 +72,69 @@ public String bigIntegerShape(BigIntegerShape shape) {
return unsupportedShape(shape);
}

@Override
public String shortShape(ShortShape shape) {
String base = super.shortShape(shape);
if (isAwsQueryCompat) {
return "_toNum(" + base + ")";
}
return base;
}

@Override
public String integerShape(IntegerShape shape) {
String base = super.integerShape(shape);
if (isAwsQueryCompat) {
return "_toNum(" + base + ")";
}
return base;
}

@Override
public String longShape(LongShape shape) {
String base = super.longShape(shape);
if (isAwsQueryCompat) {
return "_toNum(" + base + ")";
}
return base;
}

@Override
public String floatShape(FloatShape shape) {
String base = super.floatShape(shape);
if (isAwsQueryCompat) {
return "_toNum(" + base + ")";
}
return base;
}

@Override
public String doubleShape(DoubleShape shape) {
String base = super.doubleShape(shape);
if (isAwsQueryCompat) {
return "_toNum(" + base + ")";
}
return base;
}

@Override
public String booleanShape(BooleanShape shape) {
String base = super.booleanShape(shape);
if (isAwsQueryCompat) {
return "_toBool(" + base + ")";
}
return base;
}

@Override
public String stringShape(StringShape shape) {
String base = super.stringShape(shape);
if (isAwsQueryCompat) {
return "_toStr(" + base + ")";
}
return base;
}

private String unsupportedShape(Shape shape) {
throw new CodegenException(String.format("Cannot serialize shape type %s on protocol, shape: %s.",
shape.getType(), shape.getId()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,28 @@ protected Format getDocumentTimestampFormat() {

@Override
protected void generateDocumentBodyShapeSerializers(GenerationContext context, Set<Shape> shapes) {
boolean isAwsQueryCompat = context.getService().hasTrait(AwsQueryCompatibleTrait.class);

AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes,
// AWS JSON does not support jsonName
new JsonShapeSerVisitor(context, (shape, name) -> name, enableSerdeElision()));
// AWS JSON does not support jsonName
new JsonShapeSerVisitor(
context,
(shape, name) -> name,
!isAwsQueryCompat && enableSerdeElision()
)
);
}

@Override
protected void generateDocumentBodyShapeDeserializers(GenerationContext context, Set<Shape> shapes) {
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes,
// AWS JSON does not support jsonName
new JsonShapeDeserVisitor(context, (shape, name) -> name, enableSerdeElision()));
// AWS JSON does not support jsonName
new JsonShapeDeserVisitor(
context,
(shape, name) -> name,
enableSerdeElision()
)
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.List;
import java.util.Set;
import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait;
import software.amazon.smithy.aws.typescript.codegen.validation.UnaryFunctionCall;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.model.knowledge.HttpBinding;
Expand Down Expand Up @@ -69,8 +70,9 @@ protected TimestampFormatTrait.Format getDocumentTimestampFormat() {

@Override
protected void generateDocumentBodyShapeSerializers(GenerationContext context, Set<Shape> shapes) {
boolean isAwsQueryCompat = context.getService().hasTrait(AwsQueryCompatibleTrait.class);
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes, new JsonShapeSerVisitor(context,
(!context.getSettings().generateServerSdk() && enableSerdeElision())));
(!context.getSettings().generateServerSdk() && !isAwsQueryCompat && enableSerdeElision())));
}

@Override
Expand Down
76 changes: 76 additions & 0 deletions packages/core/src/protocols/coercing-serializers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { _toBool, _toNum, _toStr } from "./coercing-serializers";

const consoleWarn = console.warn;

beforeAll(() => {
console.warn = () => {};
});

afterAll(() => {
console.warn = consoleWarn;
});

describe(_toBool.name, () => {
it("ignores nullish", () => {
expect(_toBool(null)).toBe(null);
expect(_toBool(undefined)).toBe(undefined);
});

it("converts strings", () => {
expect(_toBool("false")).toEqual(false);
expect(_toBool("true")).toEqual(true);

expect(_toBool("False")).toEqual(false);
expect(_toBool("True")).toEqual(true);

expect(_toBool("")).toEqual(false);
expect(_toBool("a")).toEqual(true); // warns
});

it("does not convert numbers", () => {
expect(_toBool(0)).toEqual(0);
expect(_toBool(1)).toEqual(1);
});
});

describe(_toStr.name, () => {
it("ignores nullish", () => {
expect(_toStr(null)).toBe(null);
expect(_toStr(undefined)).toBe(undefined);
});

it("converts numbers", () => {
expect(_toStr(0)).toEqual("0");
expect(_toStr(1)).toEqual("1");
});

it("converts booleans", () => {
expect(_toStr(false)).toEqual("false");
expect(_toStr(true)).toEqual("true");
});
});

describe(_toNum.name, () => {
it("ignores nullish", () => {
expect(_toNum(null)).toBe(null);
expect(_toNum(undefined)).toBe(undefined);
});

it("converts numeric strings", () => {
expect(_toNum("1234")).toEqual(1234);
expect(_toNum("1234.56")).toEqual(1234.56);
});

it("does not convert prefix-numeric strings", () => {
expect(_toNum("1234abc")).toEqual("1234abc");
expect(_toNum("1234.56abc")).toEqual("1234.56abc");
});

it("does not convert non-numeric strings", () => {
expect(_toNum("abcdef")).toEqual("abcdef");
});
it("does not convert bools", () => {
expect(_toNum(false)).toEqual(false);
expect(_toNum(true)).toEqual(true);
});
});
72 changes: 72 additions & 0 deletions packages/core/src/protocols/coercing-serializers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @internal
*
* Used for awsQueryCompatibility trait.
*/
export const _toStr = (val: unknown): string | undefined => {
if (val == null) {
return val as undefined;
}
if (typeof val === "number" || typeof val === "bigint") {
const warning = new Error(`Received number ${val} where a string was expected.`);
warning.name = "Warning";
console.warn(warning);
return String(val);
}
if (typeof val === "boolean") {
const warning = new Error(`Received boolean ${val} where a string was expected.`);
warning.name = "Warning";
console.warn(warning);
return String(val);
}
return val as string;
};

/**
* @internal
*
* Used for awsQueryCompatibility trait.
*/
export const _toBool = (val: unknown): boolean | undefined => {
if (val == null) {
return val as undefined;
}
if (typeof val === "number") {
// transmit to service to be rejected.
}
if (typeof val === "string") {
const lowercase = val.toLowerCase();
if (val !== "" && lowercase !== "false" && lowercase !== "true") {
const warning = new Error(`Received string "${val}" where a boolean was expected.`);
warning.name = "Warning";
console.warn(warning);
}
return val !== "" && lowercase !== "false";
}
return val as boolean;
};

/**
* @internal
*
* Used for awsQueryCompatibility trait.
*/
export const _toNum = (val: unknown): number | undefined => {
if (val == null) {
return val as undefined;
}
if (typeof val === "boolean") {
// transmit to service to be rejected.
}
if (typeof val === "string") {
const num = Number(val);
if (num.toString() !== val) {
const warning = new Error(`Received string "${val}" where a number was expected.`);
warning.name = "Warning";
console.warn(warning);
return val as unknown as undefined;
}
return num;
}
return val as number;
};
1 change: 1 addition & 0 deletions packages/core/src/protocols/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./coercing-serializers";
export * from "./json/awsExpectUnion";

0 comments on commit 88213ab

Please sign in to comment.