Skip to content

Commit

Permalink
feat: xml codegen reduction
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed Dec 15, 2023
1 parent ad9fb3c commit a7c7213
Show file tree
Hide file tree
Showing 12 changed files with 4,435 additions and 4,588 deletions.
8,746 changes: 4,246 additions & 4,500 deletions clients/client-s3/src/protocols/Aws_restXml.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
import software.amazon.smithy.typescript.codegen.integration.HttpRpcProtocolGenerator;
import software.amazon.smithy.typescript.codegen.util.StringStore;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
Expand Down Expand Up @@ -144,8 +145,15 @@ protected void serializeInputDocument(
inputStructure.accept(new QueryMemberSerVisitor(context, "input", Format.DATE_TIME)));
// Set the protocol required values.
ServiceShape serviceShape = context.getService();
writer.write("Action: $S,", operation.getId().getName(serviceShape));
writer.write("Version: $S,", serviceShape.getVersion());
StringStore stringStore = context.getStringStore();
writer.write(
"[" + stringStore.var("Action") + "]: $L,",
stringStore.var(operation.getId().getName(serviceShape))
);
writer.write(
"[" + stringStore.var("Version") + "]: $L,",
stringStore.var(serviceShape.getVersion())
);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import software.amazon.smithy.typescript.codegen.integration.HttpProtocolGeneratorUtils;
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator;
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;
import software.amazon.smithy.typescript.codegen.util.StringStore;
import software.amazon.smithy.utils.IoUtils;
import software.amazon.smithy.utils.SmithyInternalApi;

Expand Down Expand Up @@ -247,8 +248,15 @@ static boolean generateUndefinedQueryInputBody(GenerationContext context, Operat
writer.openBlock("const body = buildFormUrlencodedString({", "});", () -> {
// Set the protocol required values.
ServiceShape serviceShape = context.getService();
writer.write("Action: $S,", operation.getId().getName(serviceShape));
writer.write("Version: $S,", serviceShape.getVersion());
StringStore stringStore = context.getStringStore();
writer.write(
"[" + stringStore.var("Action") + "]: $L,",
stringStore.var(operation.getId().getName(serviceShape))
);
writer.write(
"[" + stringStore.var("Version") + "]: $L,",
stringStore.var(serviceShape.getVersion())
);
});

return true;
Expand Down Expand Up @@ -277,7 +285,7 @@ static boolean writeXmlNamespace(GenerationContext context, Shape shape, String
if (prefix.isPresent()) {
xmlns += ":" + prefix.get();
}
writer.write("$L.addAttribute($S, $S);", nodeName, xmlns, trait.getUri());
writer.write("$L.a($S, $S);", nodeName, xmlns, trait.getUri());
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import software.amazon.smithy.model.traits.TimestampFormatTrait.Format;
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
import software.amazon.smithy.typescript.codegen.integration.HttpRpcProtocolGenerator;
import software.amazon.smithy.typescript.codegen.util.StringStore;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
Expand Down Expand Up @@ -113,6 +114,10 @@ public void generateSharedComponents(GenerationContext context) {
writer.openBlock("if (output.statusCode == 404) {", "}", () -> writer.write("return 'NotFound';"));
});
writer.write("");
writer.write(
context.getStringStore().getGeneratedCode()
);
writer.write("");
}

@Override
Expand Down Expand Up @@ -148,8 +153,15 @@ protected void serializeInputDocument(
inputStructure.accept(new QueryMemberSerVisitor(context, "input", Format.DATE_TIME)));
// Set the protocol required values.
ServiceShape serviceShape = context.getService();
writer.write("Action: $S,", operation.getId().getName(serviceShape));
writer.write("Version: $S,", serviceShape.getVersion());
StringStore stringStore = context.getStringStore();
writer.write(
"[" + stringStore.var("Action") + "]: $L,",
stringStore.var(operation.getId().getName(serviceShape))
);
writer.write(
"[" + stringStore.var("Version") + "]: $L,",
stringStore.var(serviceShape.getVersion())
);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import software.amazon.smithy.model.traits.XmlNameTrait;
import software.amazon.smithy.typescript.codegen.TypeScriptWriter;
import software.amazon.smithy.typescript.codegen.integration.HttpBindingProtocolGenerator;
import software.amazon.smithy.typescript.codegen.util.StringStore;
import software.amazon.smithy.utils.SmithyInternalApi;


Expand Down Expand Up @@ -65,9 +66,7 @@
@SmithyInternalApi
final class AwsRestXml extends HttpBindingProtocolGenerator {

AwsRestXml() {
super(true);
}
AwsRestXml() { super(true); }

@Override
protected String getDocumentContentType() {
Expand Down Expand Up @@ -96,7 +95,8 @@ protected void generateDocumentBodyShapeSerializers(GenerationContext context, S

@Override
protected void generateDocumentBodyShapeDeserializers(GenerationContext context, Set<Shape> shapes) {
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes, new XmlShapeDeserVisitor(context));
AwsProtocolUtils.generateDocumentBodyShapeSerde(context, shapes,
new XmlShapeDeserVisitor(context));
}

@Override
Expand Down Expand Up @@ -127,6 +127,10 @@ public void generateSharedComponents(GenerationContext context) {
writer.openBlock("if (output.statusCode == 404) {", "}", () -> writer.write("return 'NotFound';"));
});
writer.write("");
writer.write(
context.getStringStore().getGeneratedCode()
);
writer.write("");
}

@Override
Expand Down Expand Up @@ -196,7 +200,8 @@ private void serializeDocumentBody(
ShapeId inputShapeId = documentBindings.get(0).getMember().getContainer();

// Start with the XML declaration.
writer.write("body = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\";");
String xmlDeclVar = STRING_STORE.var("<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>");
writer.write("body = $L;", xmlDeclVar);

writer.addImport("XmlNode", "__XmlNode", "@aws-sdk/xml-builder");

Expand All @@ -205,13 +210,13 @@ private void serializeDocumentBody(
String nodeName = inputShape.getTrait(XmlNameTrait.class)
.map(XmlNameTrait::getValue)
.orElse(inputShapeId.getName(serviceShape));
writer.write("const bodyNode = new __XmlNode($S);", nodeName);
writer.write("const bn = new __XmlNode($L);", STRING_STORE.var(nodeName));

// Add @xmlNamespace value of the service to the root node,
// fall back to one from the input shape.
boolean serviceXmlns = AwsProtocolUtils.writeXmlNamespace(context, serviceShape, "bodyNode");
boolean serviceXmlns = AwsProtocolUtils.writeXmlNamespace(context, serviceShape, "bn");
if (!serviceXmlns) {
AwsProtocolUtils.writeXmlNamespace(context, inputShape, "bodyNode");
AwsProtocolUtils.writeXmlNamespace(context, inputShape, "bn");
}

XmlShapeSerVisitor shapeSerVisitor = new XmlShapeSerVisitor(context);
Expand All @@ -220,18 +225,16 @@ private void serializeDocumentBody(
MemberShape memberShape = binding.getMember();
// The name of the member to get from the input shape.
String memberName = symbolProvider.toMemberName(memberShape);
String inputLocation = "input." + memberName;
String inputLocation = "input[" + STRING_STORE.var(memberName) + "]";

// Handle if the member is an idempotency token that should be auto-filled.
AwsProtocolUtils.writeIdempotencyAutofill(context, memberShape, inputLocation);

writer.openBlock("if ($L !== undefined) {", "}", inputLocation, () -> {
shapeSerVisitor.serializeNamedMember(context, memberName, memberShape, () -> inputLocation);
});
shapeSerVisitor.serializeNamedMember(context, memberName, memberShape, () -> inputLocation);
}

// Append the generated XML to the body.
writer.write("body += bodyNode.toString();");
writer.write("body += bn.toString();");
}

@Override
Expand Down Expand Up @@ -286,15 +289,16 @@ private void serializePayload(
member.hasTrait(XmlNameTrait.class)
&& !member.getTrait(XmlNameTrait.class).get().getValue().equals(targetName)
) {
writer.write("contents = contents.withName($S);", member.getTrait(XmlNameTrait.class).get().getValue());
writer.write("contents = contents.n($S);", member.getTrait(XmlNameTrait.class).get().getValue());
}

// XmlNode will serialize Structure and non-streaming Union payloads as XML documents.
if (target instanceof StructureShape
|| (target instanceof UnionShape && !target.hasTrait(StreamingTrait.class))
) {
// Start with the XML declaration.
writer.write("body = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\";");
String xmlDeclVar = STRING_STORE.var("<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>");
writer.write("body = $L;", xmlDeclVar);

// Add @xmlNamespace value of the service to the root node,
// fall back to one from the payload target.
Expand Down Expand Up @@ -363,7 +367,7 @@ private void deserializeDocumentBody(

shapeVisitor.deserializeNamedMember(context, memberName, memberShape, "data", (dataSource, visitor) -> {
TypeScriptWriter writer = context.getWriter();
writer.write("contents.$L = $L;", memberName, target.accept(visitor));
writer.write("contents[$L] = $L;", STRING_STORE.var(memberName), target.accept(visitor));
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ public void generateSharedComponents(GenerationContext context) {
if (context.getService().hasTrait(AwsQueryCompatibleTrait.class)) {
AwsProtocolUtils.generateJsonParseBodyWithQueryHeader(context);
}
writer.write(
context.getStringStore().getGeneratedCode()
);
writer.write("");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import software.amazon.smithy.typescript.codegen.integration.DocumentMemberSerVisitor;
import software.amazon.smithy.typescript.codegen.integration.DocumentShapeSerVisitor;
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;
import software.amazon.smithy.typescript.codegen.util.StringStore;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
Expand All @@ -49,6 +50,7 @@
@SmithyInternalApi
class QueryShapeSerVisitor extends DocumentShapeSerVisitor {
private static final Format TIMESTAMP_FORMAT = Format.DATE_TIME;
private StringStore stringStore = getContext().getStringStore();

QueryShapeSerVisitor(GenerationContext context) {
super(context);
Expand Down Expand Up @@ -175,7 +177,7 @@ protected void serializeStructure(GenerationContext context, StructureShape shap

// Serialize every member of the structure if present.
shape.getAllMembers().forEach((memberName, memberShape) -> {
String inputLocation = "input." + memberName;
String inputLocation = "input[" + stringStore.var(memberName) + "]";

// Handle if the member is an idempotency token that should be auto-filled.
AwsProtocolUtils.writeIdempotencyAutofill(context, memberShape, inputLocation);
Expand Down Expand Up @@ -208,7 +210,10 @@ private void serializeNamedMember(
if (inputVisitor.visitSuppliesEntryList(target)) {
serializeNamedMemberEntryList(context, locationName, memberShape, target, inputVisitor, inputLocation);
} else {
serializeNamedMemberValue(context, locationName, "input." + memberName, memberShape, target);
serializeNamedMemberValue(
context, locationName,
"input[" + stringStore.var(memberName) + "]", memberShape, target
);
}
}

Expand All @@ -227,7 +232,7 @@ private void serializeNamedMemberValue(
TIMESTAMP_FORMAT, dataSource)
: target.accept(getMemberVisitor(dataSource));

writer.write("entries[$S] = $L;", locationName, valueProvider);
writer.write("entries[$L] = $L;", stringStore.var(locationName), valueProvider);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ public void generateSharedComponents(GenerationContext context) {
writer.addUseImports(getApplicationProtocol().getResponseType());
writer.addImport("take", null, TypeScriptDependency.AWS_SMITHY_CLIENT);
writer.write(IoUtils.readUtf8Resource(getClass(), "load-json-error-code-stub.ts"));

writer.write(
context.getStringStore().getGeneratedCode()
);
writer.write("");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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.typescript.codegen.util.StringStore;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
Expand All @@ -52,10 +53,12 @@
@SmithyInternalApi
final class XmlMemberSerVisitor extends DocumentMemberSerVisitor {
private final GenerationContext context;
private final StringStore stringStore;

XmlMemberSerVisitor(GenerationContext context, String dataSource, Format defaultTimestampFormat) {
super(context, dataSource, defaultTimestampFormat);
this.context = context;
this.stringStore = context.getStringStore();
}

@Override
Expand Down Expand Up @@ -121,7 +124,7 @@ String getAsXmlText(Shape shape, String dataSource) {
TypeScriptWriter writer = getContext().getWriter();
writer.addImport("XmlNode", "__XmlNode", "@aws-sdk/xml-builder");
writer.addImport("XmlText", "__XmlText", "@aws-sdk/xml-builder");
return "__XmlNode.of(\"" + nodeName + "\", " + dataSource + ")";
return "__XmlNode.of(" + stringStore.var(nodeName) + ", " + dataSource + ")";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import software.amazon.smithy.typescript.codegen.integration.DocumentMemberDeserVisitor;
import software.amazon.smithy.typescript.codegen.integration.DocumentShapeDeserVisitor;
import software.amazon.smithy.typescript.codegen.integration.ProtocolGenerator.GenerationContext;
import software.amazon.smithy.typescript.codegen.util.StringStore;
import software.amazon.smithy.utils.SmithyInternalApi;

/**
Expand All @@ -57,11 +58,14 @@
*/
@SmithyInternalApi
final class XmlShapeDeserVisitor extends DocumentShapeDeserVisitor {
private StringStore stringStore;

XmlShapeDeserVisitor(GenerationContext context) {
super(context);
stringStore = context.getStringStore();
}


private DocumentMemberDeserVisitor getMemberVisitor(MemberShape memberShape, String dataSource) {
return new XmlMemberDeserVisitor(getContext(), memberShape, dataSource, Format.DATE_TIME);
}
Expand Down Expand Up @@ -156,7 +160,7 @@ protected void deserializeStructure(GenerationContext context, StructureShape sh
// Grab the target shape so we can use a member deserializer on it.
Shape target = context.getModel().expectShape(memberShape.getTarget());
deserializeNamedMember(context, memberName, memberShape, "output", (dataSource, visitor) -> {
writer.write("contents.$L = $L;", memberName, target.accept(visitor));
writer.write("contents[$L] = $L;", stringStore.var(memberName), target.accept(visitor));
});
});

Expand Down Expand Up @@ -204,7 +208,8 @@ void deserializeNamedMember(
// Build a string based on the traits that represents the location.
// Note we don't need to handle @xmlAttribute here because the parser flattens
// attributes in to the main structure.
StringBuilder sourceBuilder = new StringBuilder(inputLocation).append("['").append(locationName).append("']");
StringBuilder sourceBuilder = new StringBuilder(inputLocation)
.append("[").append(stringStore.var(locationName)).append("]");
// Track any locations we need to validate on our way to the final element.
List<String> locationsToValidate = new ArrayList<>();

Expand All @@ -214,7 +219,8 @@ void deserializeNamedMember(
locationsToValidate.add(sourceBuilder.toString());
// Update the target element.
String targetLocation = getUnnamedAggregateTargetLocation(model, target);
sourceBuilder.append("['").append(targetLocation).append("']");

sourceBuilder.append("[").append(stringStore.var(targetLocation)).append("]");
}

boolean canMemberParsed = handleEmptyTags(
Expand All @@ -227,7 +233,7 @@ void deserializeNamedMember(
locationsToValidate.add(source);
// Build a string of the elements to validate before deserializing.
String validationStatement = locationsToValidate.stream()
.map(location -> location + " !== undefined")
.map(location -> location + " != null")
.collect(Collectors.joining(" && "));
String ifOrElseIfStatement = canMemberParsed ? "else if" : "if";
writer.openBlock("$L ($L) {", "}", ifOrElseIfStatement, validationStatement, () -> {
Expand Down Expand Up @@ -283,14 +289,14 @@ private boolean handleEmptyTags(
if (target instanceof MapShape || target instanceof CollectionShape || target instanceof UnionShape) {
writer.openBlock("if ($L.$L === \"\") {", "}", inputLocation, locationName, () -> {
if (target instanceof MapShape) {
writer.write("contents.$L = {};", memberName);
writer.write("contents[$L] = {};", stringStore.var(memberName));
} else if (target instanceof CollectionShape) {
if (isWithinUnion) {
writer.openBlock("return {", "};", () -> {
writer.write("$L: [],", memberName);
writer.write("[$L]: [],", stringStore.var(memberName));
});
} else {
writer.write("contents.$L = [];", memberName);
writer.write("contents[$L] = [];", stringStore.var(memberName));
}
} else if (target instanceof UnionShape) {
writer.write("// Pass empty tags.");
Expand Down
Loading

0 comments on commit a7c7213

Please sign in to comment.